From 19fe0ddf024bf761e50dc300b02903068ea3a2cf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:31:57 +0000 Subject: [PATCH 01/26] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 28 ++++++++ .github/workflows/release-doctor.yml | 21 ++++++ .release-please-manifest.json | 3 + .stats.yml | 2 +- CONTRIBUTING.md | 4 +- README.md | 14 ++-- bin/check-release-environment | 21 ++++++ pyproject.toml | 6 +- release-please-config.json | 66 +++++++++++++++++++ src/docstrange/_files.py | 2 +- src/docstrange/_version.py | 2 +- src/docstrange/resources/api/api.py | 8 +-- src/docstrange/resources/api/v1/classify.py | 8 +-- .../resources/api/v1/extract/extract.py | 8 +-- .../resources/api/v1/extract/results.py | 8 +-- src/docstrange/resources/api/v1/v1.py | 8 +-- src/docstrange/resources/chat.py | 8 +-- 17 files changed, 178 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 release-please-config.json diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..1be9543 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,28 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/NanoNets/docstrange-python/actions/workflows/publish-pypi.yml +name: Publish PyPI +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: '0.9.13' + + - name: Publish to PyPI + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..7f49e8e --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'NanoNets/docstrange-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v6 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..1332969 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2d4c88e..6eceaf7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: e00c8070feb190d613cf77e700818950 +config_hash: c087f5b6085f5869d1403c39fc1e3db2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b9c275..62e046f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```sh -$ pip install git+ssh://git@github.com/stainless-sdks/docstrange-python.git +$ pip install git+ssh://git@github.com/NanoNets/docstrange-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -120,7 +120,7 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/docstrange-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/NanoNets/docstrange-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually diff --git a/README.md b/README.md index 76d3c24..d10f2e4 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ The full API of this library can be found in [api.md](api.md). ## Installation ```sh -# install from this staging repo -pip install git+ssh://git@github.com/stainless-sdks/docstrange-python.git +# install from the production repo +pip install git+ssh://git@github.com/NanoNets/docstrange-python.git ``` > [!NOTE] @@ -79,8 +79,8 @@ By default, the async client uses `httpx` for HTTP requests. However, for improv You can enable this by installing `aiohttp`: ```sh -# install from this staging repo -pip install 'docstrange[aiohttp] @ git+ssh://git@github.com/stainless-sdks/docstrange-python.git' +# install from the production repo +pip install 'docstrange[aiohttp] @ git+ssh://git@github.com/NanoNets/docstrange-python.git' ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: @@ -271,9 +271,9 @@ extract = response.parse() # get the object that `api.v1.extract.sync()` would print(extract.record_id) ``` -These methods return an [`APIResponse`](https://github.com/stainless-sdks/docstrange-python/tree/main/src/docstrange/_response.py) object. +These methods return an [`APIResponse`](https://github.com/NanoNets/docstrange-python/tree/main/src/docstrange/_response.py) object. -The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/docstrange-python/tree/main/src/docstrange/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. +The async client returns an [`AsyncAPIResponse`](https://github.com/NanoNets/docstrange-python/tree/main/src/docstrange/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. #### `.with_streaming_response` @@ -379,7 +379,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/docstrange-python/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/NanoNets/docstrange-python/issues) with questions, bugs, or suggestions. ### Determining the installed version diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..b845b0f --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/pyproject.toml b/pyproject.toml index c5df32e..760311d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,8 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/stainless-sdks/docstrange-python" -Repository = "https://github.com/stainless-sdks/docstrange-python" +Homepage = "https://github.com/NanoNets/docstrange-python" +Repository = "https://github.com/NanoNets/docstrange-python" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] @@ -112,7 +112,7 @@ path = "README.md" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] # replace relative links with absolute links pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' -replacement = '[\1](https://github.com/stainless-sdks/docstrange-python/tree/main/\g<2>)' +replacement = '[\1](https://github.com/NanoNets/docstrange-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..24a8f54 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,66 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/docstrange/_version.py" + ] +} \ No newline at end of file diff --git a/src/docstrange/_files.py b/src/docstrange/_files.py index 48c137c..4ea5ccb 100644 --- a/src/docstrange/_files.py +++ b/src/docstrange/_files.py @@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/stainless-sdks/docstrange-python/tree/main#file-uploads" + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/NanoNets/docstrange-python/tree/main#file-uploads" ) from None diff --git a/src/docstrange/_version.py b/src/docstrange/_version.py index 7b3567a..fc8a3e5 100644 --- a/src/docstrange/_version.py +++ b/src/docstrange/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "docstrange" -__version__ = "0.0.1" +__version__ = "0.0.1" # x-release-please-version diff --git a/src/docstrange/resources/api/api.py b/src/docstrange/resources/api/api.py index 2156132..b771b29 100644 --- a/src/docstrange/resources/api/api.py +++ b/src/docstrange/resources/api/api.py @@ -27,7 +27,7 @@ def with_raw_response(self) -> APIResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return APIResourceWithRawResponse(self) @@ -36,7 +36,7 @@ def with_streaming_response(self) -> APIResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return APIResourceWithStreamingResponse(self) @@ -52,7 +52,7 @@ def with_raw_response(self) -> AsyncAPIResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncAPIResourceWithRawResponse(self) @@ -61,7 +61,7 @@ def with_streaming_response(self) -> AsyncAPIResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncAPIResourceWithStreamingResponse(self) diff --git a/src/docstrange/resources/api/v1/classify.py b/src/docstrange/resources/api/v1/classify.py index b31e9d9..fdd4c7a 100644 --- a/src/docstrange/resources/api/v1/classify.py +++ b/src/docstrange/resources/api/v1/classify.py @@ -39,7 +39,7 @@ def with_raw_response(self) -> ClassifyResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return ClassifyResourceWithRawResponse(self) @@ -48,7 +48,7 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return ClassifyResourceWithStreamingResponse(self) @@ -166,7 +166,7 @@ def with_raw_response(self) -> AsyncClassifyResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncClassifyResourceWithRawResponse(self) @@ -175,7 +175,7 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncClassifyResourceWithStreamingResponse(self) diff --git a/src/docstrange/resources/api/v1/extract/extract.py b/src/docstrange/resources/api/v1/extract/extract.py index adc79ea..94ced26 100644 --- a/src/docstrange/resources/api/v1/extract/extract.py +++ b/src/docstrange/resources/api/v1/extract/extract.py @@ -56,7 +56,7 @@ def with_raw_response(self) -> ExtractResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return ExtractResourceWithRawResponse(self) @@ -65,7 +65,7 @@ def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return ExtractResourceWithStreamingResponse(self) @@ -401,7 +401,7 @@ def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncExtractResourceWithRawResponse(self) @@ -410,7 +410,7 @@ def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncExtractResourceWithStreamingResponse(self) diff --git a/src/docstrange/resources/api/v1/extract/results.py b/src/docstrange/resources/api/v1/extract/results.py index 434ba0b..51832fe 100644 --- a/src/docstrange/resources/api/v1/extract/results.py +++ b/src/docstrange/resources/api/v1/extract/results.py @@ -31,7 +31,7 @@ def with_raw_response(self) -> ResultsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return ResultsResourceWithRawResponse(self) @@ -40,7 +40,7 @@ def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return ResultsResourceWithStreamingResponse(self) @@ -141,7 +141,7 @@ def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncResultsResourceWithRawResponse(self) @@ -150,7 +150,7 @@ def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncResultsResourceWithStreamingResponse(self) diff --git a/src/docstrange/resources/api/v1/v1.py b/src/docstrange/resources/api/v1/v1.py index 496bd48..1dbb99b 100644 --- a/src/docstrange/resources/api/v1/v1.py +++ b/src/docstrange/resources/api/v1/v1.py @@ -39,7 +39,7 @@ def with_raw_response(self) -> V1ResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return V1ResourceWithRawResponse(self) @@ -48,7 +48,7 @@ def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return V1ResourceWithStreamingResponse(self) @@ -68,7 +68,7 @@ def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncV1ResourceWithRawResponse(self) @@ -77,7 +77,7 @@ def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncV1ResourceWithStreamingResponse(self) diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py index c25880d..0a892c1 100644 --- a/src/docstrange/resources/chat.py +++ b/src/docstrange/resources/chat.py @@ -29,7 +29,7 @@ def with_raw_response(self) -> ChatResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return ChatResourceWithRawResponse(self) @@ -38,7 +38,7 @@ def with_streaming_response(self) -> ChatResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return ChatResourceWithStreamingResponse(self) @@ -109,7 +109,7 @@ def with_raw_response(self) -> AsyncChatResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers """ return AsyncChatResourceWithRawResponse(self) @@ -118,7 +118,7 @@ def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/docstrange-python#with_streaming_response + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response """ return AsyncChatResourceWithStreamingResponse(self) From 2c177c784f8492fd77ae6371e2cf229f5fd6d151 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:32:16 +0000 Subject: [PATCH 02/26] chore: update SDK settings --- .stats.yml | 2 +- README.md | 13 ++-- pyproject.toml | 2 +- requirements-dev.lock | 12 +-- uv.lock | 170 +++++++++++++++++++++--------------------- 5 files changed, 98 insertions(+), 101 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6eceaf7..18ffc41 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: c087f5b6085f5869d1403c39fc1e3db2 +config_hash: ed1c4120702af3765a79005ac3ed79a9 diff --git a/README.md b/README.md index d10f2e4..751b894 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Docstrange Python API library -[![PyPI version](https://img.shields.io/pypi/v/docstrange.svg?label=pypi%20(stable))](https://pypi.org/project/docstrange/) +[![PyPI version](https://img.shields.io/pypi/v/docstrange-api.svg?label=pypi%20(stable))](https://pypi.org/project/docstrange-api/) The Docstrange Python library provides convenient access to the Docstrange REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, @@ -16,13 +16,10 @@ The full API of this library can be found in [api.md](api.md). ## Installation ```sh -# install from the production repo -pip install git+ssh://git@github.com/NanoNets/docstrange-python.git +# install from PyPI +pip install docstrange-api ``` -> [!NOTE] -> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install docstrange` - ## Usage The full API of this library can be found in [api.md](api.md). @@ -79,8 +76,8 @@ By default, the async client uses `httpx` for HTTP requests. However, for improv You can enable this by installing `aiohttp`: ```sh -# install from the production repo -pip install 'docstrange[aiohttp] @ git+ssh://git@github.com/NanoNets/docstrange-python.git' +# install from PyPI +pip install docstrange-api[aiohttp] ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: diff --git a/pyproject.toml b/pyproject.toml index 760311d..0d11a7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "docstrange" +name = "docstrange-api" version = "0.0.1" description = "The official Python library for the docstrange API" dynamic = ["readme"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 01d01bc..e92af26 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -5,7 +5,7 @@ annotated-types==0.7.0 # via pydantic anyio==4.12.1 # via - # docstrange + # docstrange-api # httpx backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' # via pytest-asyncio @@ -17,7 +17,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # via pytest dirty-equals==0.11 distro==1.9.0 - # via docstrange + # via docstrange-api exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # anyio @@ -30,7 +30,7 @@ httpcore==1.0.9 # via httpx httpx==0.28.1 # via - # docstrange + # docstrange-api # respx idna==3.11 # via @@ -59,7 +59,7 @@ pathspec==1.0.3 pluggy==1.6.0 # via pytest pydantic==2.12.5 - # via docstrange + # via docstrange-api pydantic-core==2.41.5 # via pydantic pygments==2.19.2 @@ -86,7 +86,7 @@ ruff==0.14.13 six==1.17.0 ; python_full_version < '3.10' # via python-dateutil sniffio==1.3.1 - # via docstrange + # via docstrange-api time-machine==2.19.0 ; python_full_version < '3.10' time-machine==3.2.0 ; python_full_version >= '3.10' tomli==2.4.0 ; python_full_version < '3.11' @@ -96,7 +96,7 @@ tomli==2.4.0 ; python_full_version < '3.11' typing-extensions==4.15.0 # via # anyio - # docstrange + # docstrange-api # exceptiongroup # mypy # pydantic diff --git a/uv.lock b/uv.lock index 67db705..8e4d6e0 100644 --- a/uv.lock +++ b/uv.lock @@ -2,17 +2,17 @@ version = 1 revision = 3 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version < '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version < '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version < '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version < '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version < '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version < '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] conflicts = [[ - { package = "docstrange", group = "pydantic-v1" }, - { package = "docstrange", group = "pydantic-v2" }, + { package = "docstrange-api", group = "pydantic-v1" }, + { package = "docstrange-api", group = "pydantic-v2" }, ]] [[package]] @@ -31,7 +31,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, - { name = "async-timeout", marker = "python_full_version < '3.11' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "async-timeout", marker = "python_full_version < '3.11' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, { name = "attrs" }, { name = "frozenlist" }, { name = "multidict" }, @@ -167,7 +167,7 @@ version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ @@ -188,9 +188,9 @@ name = "anyio" version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ @@ -261,15 +261,15 @@ wheels = [ ] [[package]] -name = "docstrange" +name = "docstrange-api" version = "0.0.1" source = { editable = "." } dependencies = [ { name = "anyio" }, { name = "distro" }, { name = "httpx" }, - { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-10-docstrange-pydantic-v1'" }, - { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, + { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-14-docstrange-api-pydantic-v1'" }, + { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, { name = "sniffio" }, { name = "typing-extensions" }, ] @@ -286,16 +286,16 @@ dev = [ { name = "importlib-metadata" }, { name = "mypy" }, { name = "pyright" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest-asyncio", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest-asyncio", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest-asyncio", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest-asyncio", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, { name = "pytest-xdist" }, { name = "respx" }, { name = "rich" }, { name = "ruff" }, - { name = "time-machine", version = "2.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "time-machine", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "time-machine", version = "2.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "time-machine", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] pydantic-v1 = [ { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" } }, @@ -342,7 +342,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -583,10 +583,10 @@ name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ @@ -601,7 +601,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "mdurl", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ @@ -613,13 +613,13 @@ name = "markdown-it-py" version = "4.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "mdurl", marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ @@ -640,7 +640,7 @@ name = "multidict" version = "6.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } wheels = [ @@ -798,7 +798,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } @@ -1019,7 +1019,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "typing-extensions", marker = "extra == 'group-10-docstrange-pydantic-v1'" }, + { name = "typing-extensions", marker = "extra == 'group-14-docstrange-api-pydantic-v1'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7b/da/fd89f987a376c807cd81ea0eff4589aade783bbb702637b4734ef2c743a2/pydantic-1.10.26.tar.gz", hash = "sha256:8c6aa39b494c5af092e690127c283d84f363ac36017106a9e66cb33a22ac412e", size = 357906, upload-time = "2025-12-18T15:47:46.557Z" } wheels = [ @@ -1061,17 +1061,17 @@ name = "pydantic" version = "2.12.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version < '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version < '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version < '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version < '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] dependencies = [ - { name = "annotated-types", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, - { name = "pydantic-core", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, - { name = "typing-extensions", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, - { name = "typing-inspection", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, + { name = "annotated-types", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, + { name = "pydantic-core", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, + { name = "typing-extensions", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, + { name = "typing-inspection", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ @@ -1083,7 +1083,7 @@ name = "pydantic-core" version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, + { name = "typing-extensions", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ @@ -1239,13 +1239,13 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2') or (sys_platform != 'win32' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pluggy", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2') or (sys_platform != 'win32' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pluggy", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ @@ -1257,19 +1257,19 @@ name = "pytest" version = "9.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] dependencies = [ - { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2') or (sys_platform != 'win32' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "packaging", marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pluggy", marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pygments", marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2') or (sys_platform != 'win32' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "packaging", marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pluggy", marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pygments", marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ @@ -1284,9 +1284,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "backports-asyncio-runner", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ @@ -1298,15 +1298,15 @@ name = "pytest-asyncio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version == '3.10.*' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "typing-extensions", marker = "(python_full_version >= '3.10' and python_full_version < '3.13') or (python_full_version < '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2') or (python_full_version >= '3.13' and extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "backports-asyncio-runner", marker = "python_full_version == '3.10.*' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.10' and python_full_version < '3.13') or (python_full_version < '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2') or (python_full_version >= '3.13' and extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ @@ -1319,8 +1319,8 @@ version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ @@ -1332,7 +1332,7 @@ name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "six", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ @@ -1356,8 +1356,8 @@ name = "rich" version = "14.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, - { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } @@ -1417,7 +1417,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "python-dateutil", marker = "python_full_version < '3.10' or (extra == 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2')" }, + { name = "python-dateutil", marker = "python_full_version < '3.10' or (extra == 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/1b5fdd165f61b67f445fac2a7feb0c655118edef429cd09ff5a8067f7f1d/time_machine-2.19.0.tar.gz", hash = "sha256:7c5065a8b3f2bbb449422c66ef71d114d3f909c276a6469642ecfffb6a0fcd29", size = 14576, upload-time = "2025-08-19T17:22:08.402Z" } wheels = [ @@ -1516,10 +1516,10 @@ name = "time-machine" version = "3.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-10-docstrange-pydantic-v1' and extra == 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra == 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", - "python_full_version >= '3.10' and extra != 'group-10-docstrange-pydantic-v1' and extra != 'group-10-docstrange-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'group-14-docstrange-api-pydantic-v1' and extra == 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'group-14-docstrange-api-pydantic-v1' and extra != 'group-14-docstrange-api-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/02/fc/37b02f6094dbb1f851145330460532176ed2f1dc70511a35828166c41e52/time_machine-3.2.0.tar.gz", hash = "sha256:a4ddd1cea17b8950e462d1805a42b20c81eb9aafc8f66b392dd5ce997e037d79", size = 14804, upload-time = "2025-12-17T23:33:02.599Z" } wheels = [ @@ -1670,7 +1670,7 @@ name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "extra == 'group-10-docstrange-pydantic-v2' or extra != 'group-10-docstrange-pydantic-v1'" }, + { name = "typing-extensions", marker = "extra == 'group-14-docstrange-api-pydantic-v2' or extra != 'group-14-docstrange-api-pydantic-v1'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ From c2d9004668a9eb79f6cc5a19879ed8d9928f316f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:32:34 +0000 Subject: [PATCH 03/26] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 5 +++-- .github/workflows/release-doctor.yml | 2 -- .stats.yml | 2 +- bin/check-release-environment | 4 ---- bin/publish-pypi | 6 +++++- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 1be9543..e1fd4b7 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -12,6 +12,9 @@ jobs: publish: name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v6 @@ -24,5 +27,3 @@ jobs: - name: Publish to PyPI run: | bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 7f49e8e..5be741d 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -17,5 +17,3 @@ jobs: - name: Check release environment run: | bash ./bin/check-release-environment - env: - PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.stats.yml b/.stats.yml index 18ffc41..ca198c1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: ed1c4120702af3765a79005ac3ed79a9 +config_hash: 499f6cd156a3fc23a8c12d6d6c9d8ba5 diff --git a/bin/check-release-environment b/bin/check-release-environment index b845b0f..1e951e9 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,10 +2,6 @@ errors=() -if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") -fi - lenErrors=${#errors[@]} if [[ lenErrors -gt 0 ]]; then diff --git a/bin/publish-pypi b/bin/publish-pypi index e72ca2f..5895700 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -4,4 +4,8 @@ set -eux rm -rf dist mkdir -p dist uv build -uv publish --token=$PYPI_TOKEN +if [ -n "${PYPI_TOKEN:-}" ]; then + uv publish --token=$PYPI_TOKEN +else + uv publish +fi From ec90172800803c48e72e6105e265ccd757f80314 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:32:52 +0000 Subject: [PATCH 04/26] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 5 ++--- .github/workflows/release-doctor.yml | 2 ++ .stats.yml | 2 +- bin/check-release-environment | 4 ++++ bin/publish-pypi | 6 +----- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index e1fd4b7..1be9543 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -12,9 +12,6 @@ jobs: publish: name: publish runs-on: ubuntu-latest - permissions: - contents: read - id-token: write steps: - uses: actions/checkout@v6 @@ -27,3 +24,5 @@ jobs: - name: Publish to PyPI run: | bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 5be741d..7f49e8e 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -17,3 +17,5 @@ jobs: - name: Check release environment run: | bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.stats.yml b/.stats.yml index ca198c1..18ffc41 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 499f6cd156a3fc23a8c12d6d6c9d8ba5 +config_hash: ed1c4120702af3765a79005ac3ed79a9 diff --git a/bin/check-release-environment b/bin/check-release-environment index 1e951e9..b845b0f 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,6 +2,10 @@ errors=() +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + lenErrors=${#errors[@]} if [[ lenErrors -gt 0 ]]; then diff --git a/bin/publish-pypi b/bin/publish-pypi index 5895700..e72ca2f 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -4,8 +4,4 @@ set -eux rm -rf dist mkdir -p dist uv build -if [ -n "${PYPI_TOKEN:-}" ]; then - uv publish --token=$PYPI_TOKEN -else - uv publish -fi +uv publish --token=$PYPI_TOKEN From e231259dabdfcb1ee3d818f5720d484dbc48d7fd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:33:45 +0000 Subject: [PATCH 05/26] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 5 +++-- .github/workflows/release-doctor.yml | 2 -- .stats.yml | 2 +- bin/check-release-environment | 4 ---- bin/publish-pypi | 6 +++++- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 1be9543..e1fd4b7 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -12,6 +12,9 @@ jobs: publish: name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v6 @@ -24,5 +27,3 @@ jobs: - name: Publish to PyPI run: | bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 7f49e8e..5be741d 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -17,5 +17,3 @@ jobs: - name: Check release environment run: | bash ./bin/check-release-environment - env: - PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.stats.yml b/.stats.yml index 18ffc41..ca198c1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: ed1c4120702af3765a79005ac3ed79a9 +config_hash: 499f6cd156a3fc23a8c12d6d6c9d8ba5 diff --git a/bin/check-release-environment b/bin/check-release-environment index b845b0f..1e951e9 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,10 +2,6 @@ errors=() -if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") -fi - lenErrors=${#errors[@]} if [[ lenErrors -gt 0 ]]; then diff --git a/bin/publish-pypi b/bin/publish-pypi index e72ca2f..5895700 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -4,4 +4,8 @@ set -eux rm -rf dist mkdir -p dist uv build -uv publish --token=$PYPI_TOKEN +if [ -n "${PYPI_TOKEN:-}" ]; then + uv publish --token=$PYPI_TOKEN +else + uv publish +fi From 2a9320359140a76fd94710fe9b57f0040a4eab84 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 07:08:23 +0000 Subject: [PATCH 06/26] Update config with full model mappings and docstrange branding --- .stats.yml | 2 +- README.md | 22 +-- api.md | 51 +++--- src/docstrange/_client.py | 77 ++++++--- src/docstrange/resources/__init__.py | 42 +++-- src/docstrange/resources/api/__init__.py | 33 ---- src/docstrange/resources/api/api.py | 102 ------------ src/docstrange/resources/api/v1/__init__.py | 47 ------ src/docstrange/resources/api/v1/v1.py | 134 ---------------- .../resources/{api/v1 => }/classify.py | 108 ++++++------- .../{api/v1 => }/extract/__init__.py | 0 .../resources/{api/v1 => }/extract/extract.py | 147 ++++++++---------- .../resources/{api/v1 => }/extract/results.py | 26 ++-- src/docstrange/types/__init__.py | 16 ++ src/docstrange/types/api/__init__.py | 3 - src/docstrange/types/api/v1/__init__.py | 17 -- .../types/api/v1/extraction_format_result.py | 22 --- ...response.py => batch_classify_response.py} | 6 +- ..._response.py => batch_extract_response.py} | 6 +- .../{api/v1 => }/classify_batch_params.py | 2 +- ..._sync_response.py => classify_response.py} | 6 +- .../{api/v1 => }/classify_sync_params.py | 2 +- .../types/{api/v1 => }/extract/__init__.py | 3 +- .../types/extract/extraction_list_response.py | 17 ++ .../pagination_info.py} | 17 +- .../v1 => }/extract/result_list_params.py | 0 .../v1 => }/extract/result_retrieve_params.py | 0 .../{api/v1 => }/extract_async_params.py | 2 +- .../{api/v1 => }/extract_batch_params.py | 2 +- .../types/{api/v1 => }/extract_response.py | 22 +-- .../{api/v1 => }/extract_stream_params.py | 2 +- .../{api/v1 => }/extract_stream_response.py | 0 .../types/{api/v1 => }/extract_sync_params.py | 2 +- .../types/extraction_format_result.py | 15 ++ src/docstrange/types/extraction_metadata.py | 15 ++ src/docstrange/types/extraction_result.py | 22 +++ .../types/file_classification_result.py | 24 +++ ...ation_result.py => page_classification.py} | 24 +-- tests/api_resources/api/v1/__init__.py | 1 - .../api_resources/api/v1/extract/__init__.py | 1 - .../{api => extract}/__init__.py | 0 .../{api/v1 => }/extract/test_results.py | 56 +++---- .../{api/v1 => }/test_classify.py | 53 +++---- .../{api/v1 => }/test_extract.py | 84 +++++----- tests/test_client.py | 20 +-- 45 files changed, 497 insertions(+), 756 deletions(-) delete mode 100644 src/docstrange/resources/api/__init__.py delete mode 100644 src/docstrange/resources/api/api.py delete mode 100644 src/docstrange/resources/api/v1/__init__.py delete mode 100644 src/docstrange/resources/api/v1/v1.py rename src/docstrange/resources/{api/v1 => }/classify.py (81%) rename src/docstrange/resources/{api/v1 => }/extract/__init__.py (100%) rename src/docstrange/resources/{api/v1 => }/extract/extract.py (89%) rename src/docstrange/resources/{api/v1 => }/extract/results.py (93%) delete mode 100644 src/docstrange/types/api/__init__.py delete mode 100644 src/docstrange/types/api/v1/__init__.py delete mode 100644 src/docstrange/types/api/v1/extraction_format_result.py rename src/docstrange/types/{api/v1/classify_batch_response.py => batch_classify_response.py} (76%) rename src/docstrange/types/{api/v1/extract_batch_response.py => batch_extract_response.py} (75%) rename src/docstrange/types/{api/v1 => }/classify_batch_params.py (89%) rename src/docstrange/types/{api/v1/classify_sync_response.py => classify_response.py} (71%) rename src/docstrange/types/{api/v1 => }/classify_sync_params.py (93%) rename src/docstrange/types/{api/v1 => }/extract/__init__.py (64%) create mode 100644 src/docstrange/types/extract/extraction_list_response.py rename src/docstrange/types/{api/v1/extract/result_list_response.py => extract/pagination_info.py} (50%) rename src/docstrange/types/{api/v1 => }/extract/result_list_params.py (100%) rename src/docstrange/types/{api/v1 => }/extract/result_retrieve_params.py (100%) rename src/docstrange/types/{api/v1 => }/extract_async_params.py (97%) rename src/docstrange/types/{api/v1 => }/extract_batch_params.py (92%) rename src/docstrange/types/{api/v1 => }/extract_response.py (57%) rename src/docstrange/types/{api/v1 => }/extract_stream_params.py (97%) rename src/docstrange/types/{api/v1 => }/extract_stream_response.py (100%) rename src/docstrange/types/{api/v1 => }/extract_sync_params.py (97%) create mode 100644 src/docstrange/types/extraction_format_result.py create mode 100644 src/docstrange/types/extraction_metadata.py create mode 100644 src/docstrange/types/extraction_result.py create mode 100644 src/docstrange/types/file_classification_result.py rename src/docstrange/types/{api/v1/file_classification_result.py => page_classification.py} (56%) delete mode 100644 tests/api_resources/api/v1/__init__.py delete mode 100644 tests/api_resources/api/v1/extract/__init__.py rename tests/api_resources/{api => extract}/__init__.py (100%) rename tests/api_resources/{api/v1 => }/extract/test_results.py (76%) rename tests/api_resources/{api/v1 => }/test_classify.py (78%) rename tests/api_resources/{api/v1 => }/test_extract.py (84%) diff --git a/.stats.yml b/.stats.yml index ca198c1..1774d1c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 499f6cd156a3fc23a8c12d6d6c9d8ba5 +config_hash: 5095aef18b1e8c018bc58e39d6f4b23d diff --git a/README.md b/README.md index 751b894..9532911 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It is generated with [Stainless](https://www.stainless.com/). ## Documentation -The full API of this library can be found in [api.md](api.md). +The REST API documentation can be found on [docs.nanonets.com](https://docs.nanonets.com). The full API of this library can be found in [api.md](api.md). ## Installation @@ -32,7 +32,7 @@ client = Docstrange( api_key=os.environ.get("DOCSTRANGE_API_KEY"), # This is the default and can be omitted ) -extract_response = client.api.v1.extract.sync( +extract_response = client.extract.sync( output_format="markdown", ) print(extract_response.record_id) @@ -58,7 +58,7 @@ client = AsyncDocstrange( async def main() -> None: - extract_response = await client.api.v1.extract.sync( + extract_response = await client.extract.sync( output_format="markdown", ) print(extract_response.record_id) @@ -94,7 +94,7 @@ async def main() -> None: api_key=os.environ.get("DOCSTRANGE_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - extract_response = await client.api.v1.extract.sync( + extract_response = await client.extract.sync( output_format="markdown", ) print(extract_response.record_id) @@ -122,7 +122,7 @@ from docstrange import Docstrange client = Docstrange() -client.api.v1.extract.sync( +client.extract.sync( output_format="markdown", file=Path("/path/to/file"), ) @@ -146,7 +146,7 @@ from docstrange import Docstrange client = Docstrange() try: - client.api.v1.extract.sync( + client.extract.sync( output_format="markdown", ) except docstrange.APIConnectionError as e: @@ -191,7 +191,7 @@ client = Docstrange( ) # Or, configure per-request: -client.with_options(max_retries=5).api.v1.extract.sync( +client.with_options(max_retries=5).extract.sync( output_format="markdown", ) ``` @@ -216,7 +216,7 @@ client = Docstrange( ) # Override per-request: -client.with_options(timeout=5.0).api.v1.extract.sync( +client.with_options(timeout=5.0).extract.sync( output_format="markdown", ) ``` @@ -259,12 +259,12 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from docstrange import Docstrange client = Docstrange() -response = client.api.v1.extract.with_raw_response.sync( +response = client.extract.with_raw_response.sync( output_format="markdown", ) print(response.headers.get('X-My-Header')) -extract = response.parse() # get the object that `api.v1.extract.sync()` would have returned +extract = response.parse() # get the object that `extract.sync()` would have returned print(extract.record_id) ``` @@ -279,7 +279,7 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.api.v1.extract.with_streaming_response.sync( +with client.extract.with_streaming_response.sync( output_format="markdown", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 3b4eeb8..2f2f496 100644 --- a/api.md +++ b/api.md @@ -1,60 +1,69 @@ -# API - -## V1 - -### Extract +# Extract Types: ```python -from docstrange.types.api.v1 import ( +from docstrange.types import ( + BatchExtractRequestBody, + BatchExtractResponse, ExtractRequestBody, ExtractResponse, ExtractionFormatResult, - ExtractBatchResponse, + ExtractionMetadata, + ExtractionResult, + StreamExtractRequestBody, ExtractStreamResponse, ) ``` Methods: -- client.api.v1.extract.async\_(\*\*params) -> ExtractResponse -- client.api.v1.extract.batch(\*\*params) -> ExtractBatchResponse -- client.api.v1.extract.stream(\*\*params) -> str -- client.api.v1.extract.sync(\*\*params) -> ExtractResponse +- client.extract.async\_(\*\*params) -> ExtractResponse +- client.extract.batch(\*\*params) -> BatchExtractResponse +- client.extract.stream(\*\*params) -> str +- client.extract.sync(\*\*params) -> ExtractResponse -#### Results +## Results Types: ```python -from docstrange.types.api.v1.extract import ResultListResponse +from docstrange.types.extract import ExtractionListResponse, PaginationInfo ``` Methods: -- client.api.v1.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.api.v1.extract.results.list(\*\*params) -> ResultListResponse +- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse +- client.extract.results.list(\*\*params) -> ExtractionListResponse -### Classify +# Classify Types: ```python -from docstrange.types.api.v1 import ( +from docstrange.types import ( + BatchClassifyRequestBody, + BatchClassifyResponse, + ClassifyRequestBody, + ClassifyResponse, FileClassificationResult, - ClassifyBatchResponse, - ClassifySyncResponse, + PageClassification, ) ``` Methods: -- client.api.v1.classify.batch(\*\*params) -> ClassifyBatchResponse -- client.api.v1.classify.sync(\*\*params) -> ClassifySyncResponse +- client.classify.batch(\*\*params) -> BatchClassifyResponse +- client.classify.sync(\*\*params) -> ClassifyResponse # Chat +Types: + +```python +from docstrange.types import ChatCompletionsRequest +``` + Methods: - client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index f7a66e4..4c5bcc4 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,9 +31,10 @@ ) if TYPE_CHECKING: - from .resources import api, chat + from .resources import chat, extract, classify from .resources.chat import ChatResource, AsyncChatResource - from .resources.api.api import APIResource, AsyncAPIResource + from .resources.classify import ClassifyResource, AsyncClassifyResource + from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -103,10 +104,16 @@ def __init__( ) @cached_property - def api(self) -> APIResource: - from .resources.api import APIResource + def extract(self) -> ExtractResource: + from .resources.extract import ExtractResource - return APIResource(self) + return ExtractResource(self) + + @cached_property + def classify(self) -> ClassifyResource: + from .resources.classify import ClassifyResource + + return ClassifyResource(self) @cached_property def chat(self) -> ChatResource: @@ -283,10 +290,16 @@ def __init__( ) @cached_property - def api(self) -> AsyncAPIResource: - from .resources.api import AsyncAPIResource + def extract(self) -> AsyncExtractResource: + from .resources.extract import AsyncExtractResource - return AsyncAPIResource(self) + return AsyncExtractResource(self) + + @cached_property + def classify(self) -> AsyncClassifyResource: + from .resources.classify import AsyncClassifyResource + + return AsyncClassifyResource(self) @cached_property def chat(self) -> AsyncChatResource: @@ -414,10 +427,16 @@ def __init__(self, client: Docstrange) -> None: self._client = client @cached_property - def api(self) -> api.APIResourceWithRawResponse: - from .resources.api import APIResourceWithRawResponse + def extract(self) -> extract.ExtractResourceWithRawResponse: + from .resources.extract import ExtractResourceWithRawResponse + + return ExtractResourceWithRawResponse(self._client.extract) + + @cached_property + def classify(self) -> classify.ClassifyResourceWithRawResponse: + from .resources.classify import ClassifyResourceWithRawResponse - return APIResourceWithRawResponse(self._client.api) + return ClassifyResourceWithRawResponse(self._client.classify) @cached_property def chat(self) -> chat.ChatResourceWithRawResponse: @@ -433,10 +452,16 @@ def __init__(self, client: AsyncDocstrange) -> None: self._client = client @cached_property - def api(self) -> api.AsyncAPIResourceWithRawResponse: - from .resources.api import AsyncAPIResourceWithRawResponse + def extract(self) -> extract.AsyncExtractResourceWithRawResponse: + from .resources.extract import AsyncExtractResourceWithRawResponse - return AsyncAPIResourceWithRawResponse(self._client.api) + return AsyncExtractResourceWithRawResponse(self._client.extract) + + @cached_property + def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: + from .resources.classify import AsyncClassifyResourceWithRawResponse + + return AsyncClassifyResourceWithRawResponse(self._client.classify) @cached_property def chat(self) -> chat.AsyncChatResourceWithRawResponse: @@ -452,10 +477,16 @@ def __init__(self, client: Docstrange) -> None: self._client = client @cached_property - def api(self) -> api.APIResourceWithStreamingResponse: - from .resources.api import APIResourceWithStreamingResponse + def extract(self) -> extract.ExtractResourceWithStreamingResponse: + from .resources.extract import ExtractResourceWithStreamingResponse - return APIResourceWithStreamingResponse(self._client.api) + return ExtractResourceWithStreamingResponse(self._client.extract) + + @cached_property + def classify(self) -> classify.ClassifyResourceWithStreamingResponse: + from .resources.classify import ClassifyResourceWithStreamingResponse + + return ClassifyResourceWithStreamingResponse(self._client.classify) @cached_property def chat(self) -> chat.ChatResourceWithStreamingResponse: @@ -471,10 +502,16 @@ def __init__(self, client: AsyncDocstrange) -> None: self._client = client @cached_property - def api(self) -> api.AsyncAPIResourceWithStreamingResponse: - from .resources.api import AsyncAPIResourceWithStreamingResponse + def extract(self) -> extract.AsyncExtractResourceWithStreamingResponse: + from .resources.extract import AsyncExtractResourceWithStreamingResponse + + return AsyncExtractResourceWithStreamingResponse(self._client.extract) + + @cached_property + def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: + from .resources.classify import AsyncClassifyResourceWithStreamingResponse - return AsyncAPIResourceWithStreamingResponse(self._client.api) + return AsyncClassifyResourceWithStreamingResponse(self._client.classify) @cached_property def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index a4f482d..a3172dd 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .api import ( - APIResource, - AsyncAPIResource, - APIResourceWithRawResponse, - AsyncAPIResourceWithRawResponse, - APIResourceWithStreamingResponse, - AsyncAPIResourceWithStreamingResponse, -) from .chat import ( ChatResource, AsyncChatResource, @@ -16,14 +8,36 @@ ChatResourceWithStreamingResponse, AsyncChatResourceWithStreamingResponse, ) +from .extract import ( + ExtractResource, + AsyncExtractResource, + ExtractResourceWithRawResponse, + AsyncExtractResourceWithRawResponse, + ExtractResourceWithStreamingResponse, + AsyncExtractResourceWithStreamingResponse, +) +from .classify import ( + ClassifyResource, + AsyncClassifyResource, + ClassifyResourceWithRawResponse, + AsyncClassifyResourceWithRawResponse, + ClassifyResourceWithStreamingResponse, + AsyncClassifyResourceWithStreamingResponse, +) __all__ = [ - "APIResource", - "AsyncAPIResource", - "APIResourceWithRawResponse", - "AsyncAPIResourceWithRawResponse", - "APIResourceWithStreamingResponse", - "AsyncAPIResourceWithStreamingResponse", + "ExtractResource", + "AsyncExtractResource", + "ExtractResourceWithRawResponse", + "AsyncExtractResourceWithRawResponse", + "ExtractResourceWithStreamingResponse", + "AsyncExtractResourceWithStreamingResponse", + "ClassifyResource", + "AsyncClassifyResource", + "ClassifyResourceWithRawResponse", + "AsyncClassifyResourceWithRawResponse", + "ClassifyResourceWithStreamingResponse", + "AsyncClassifyResourceWithStreamingResponse", "ChatResource", "AsyncChatResource", "ChatResourceWithRawResponse", diff --git a/src/docstrange/resources/api/__init__.py b/src/docstrange/resources/api/__init__.py deleted file mode 100644 index c0c6efa..0000000 --- a/src/docstrange/resources/api/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .api import ( - APIResource, - AsyncAPIResource, - APIResourceWithRawResponse, - AsyncAPIResourceWithRawResponse, - APIResourceWithStreamingResponse, - AsyncAPIResourceWithStreamingResponse, -) - -__all__ = [ - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", - "APIResource", - "AsyncAPIResource", - "APIResourceWithRawResponse", - "AsyncAPIResourceWithRawResponse", - "APIResourceWithStreamingResponse", - "AsyncAPIResourceWithStreamingResponse", -] diff --git a/src/docstrange/resources/api/api.py b/src/docstrange/resources/api/api.py deleted file mode 100644 index b771b29..0000000 --- a/src/docstrange/resources/api/api.py +++ /dev/null @@ -1,102 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from ... import _resource -from .v1.v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from ..._compat import cached_property - -__all__ = ["APIResource", "AsyncAPIResource"] - - -class APIResource(_resource.SyncAPIResource): - @cached_property - def v1(self) -> V1Resource: - return V1Resource(self._client) - - @cached_property - def with_raw_response(self) -> APIResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return APIResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> APIResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return APIResourceWithStreamingResponse(self) - - -class AsyncAPIResource(_resource.AsyncAPIResource): - @cached_property - def v1(self) -> AsyncV1Resource: - return AsyncV1Resource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncAPIResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncAPIResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAPIResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncAPIResourceWithStreamingResponse(self) - - -class APIResourceWithRawResponse: - def __init__(self, api: APIResource) -> None: - self._api = api - - @cached_property - def v1(self) -> V1ResourceWithRawResponse: - return V1ResourceWithRawResponse(self._api.v1) - - -class AsyncAPIResourceWithRawResponse: - def __init__(self, api: AsyncAPIResource) -> None: - self._api = api - - @cached_property - def v1(self) -> AsyncV1ResourceWithRawResponse: - return AsyncV1ResourceWithRawResponse(self._api.v1) - - -class APIResourceWithStreamingResponse: - def __init__(self, api: APIResource) -> None: - self._api = api - - @cached_property - def v1(self) -> V1ResourceWithStreamingResponse: - return V1ResourceWithStreamingResponse(self._api.v1) - - -class AsyncAPIResourceWithStreamingResponse: - def __init__(self, api: AsyncAPIResource) -> None: - self._api = api - - @cached_property - def v1(self) -> AsyncV1ResourceWithStreamingResponse: - return AsyncV1ResourceWithStreamingResponse(self._api.v1) diff --git a/src/docstrange/resources/api/v1/__init__.py b/src/docstrange/resources/api/v1/__init__.py deleted file mode 100644 index 422780c..0000000 --- a/src/docstrange/resources/api/v1/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .extract import ( - ExtractResource, - AsyncExtractResource, - ExtractResourceWithRawResponse, - AsyncExtractResourceWithRawResponse, - ExtractResourceWithStreamingResponse, - AsyncExtractResourceWithStreamingResponse, -) -from .classify import ( - ClassifyResource, - AsyncClassifyResource, - ClassifyResourceWithRawResponse, - AsyncClassifyResourceWithRawResponse, - ClassifyResourceWithStreamingResponse, - AsyncClassifyResourceWithStreamingResponse, -) - -__all__ = [ - "ExtractResource", - "AsyncExtractResource", - "ExtractResourceWithRawResponse", - "AsyncExtractResourceWithRawResponse", - "ExtractResourceWithStreamingResponse", - "AsyncExtractResourceWithStreamingResponse", - "ClassifyResource", - "AsyncClassifyResource", - "ClassifyResourceWithRawResponse", - "AsyncClassifyResourceWithRawResponse", - "ClassifyResourceWithStreamingResponse", - "AsyncClassifyResourceWithStreamingResponse", - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", -] diff --git a/src/docstrange/resources/api/v1/v1.py b/src/docstrange/resources/api/v1/v1.py deleted file mode 100644 index 1dbb99b..0000000 --- a/src/docstrange/resources/api/v1/v1.py +++ /dev/null @@ -1,134 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .classify import ( - ClassifyResource, - AsyncClassifyResource, - ClassifyResourceWithRawResponse, - AsyncClassifyResourceWithRawResponse, - ClassifyResourceWithStreamingResponse, - AsyncClassifyResourceWithStreamingResponse, -) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from .extract.extract import ( - ExtractResource, - AsyncExtractResource, - ExtractResourceWithRawResponse, - AsyncExtractResourceWithRawResponse, - ExtractResourceWithStreamingResponse, - AsyncExtractResourceWithStreamingResponse, -) - -__all__ = ["V1Resource", "AsyncV1Resource"] - - -class V1Resource(SyncAPIResource): - @cached_property - def extract(self) -> ExtractResource: - return ExtractResource(self._client) - - @cached_property - def classify(self) -> ClassifyResource: - return ClassifyResource(self._client) - - @cached_property - def with_raw_response(self) -> V1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return V1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> V1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return V1ResourceWithStreamingResponse(self) - - -class AsyncV1Resource(AsyncAPIResource): - @cached_property - def extract(self) -> AsyncExtractResource: - return AsyncExtractResource(self._client) - - @cached_property - def classify(self) -> AsyncClassifyResource: - return AsyncClassifyResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncV1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncV1ResourceWithStreamingResponse(self) - - -class V1ResourceWithRawResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - @cached_property - def extract(self) -> ExtractResourceWithRawResponse: - return ExtractResourceWithRawResponse(self._v1.extract) - - @cached_property - def classify(self) -> ClassifyResourceWithRawResponse: - return ClassifyResourceWithRawResponse(self._v1.classify) - - -class AsyncV1ResourceWithRawResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - @cached_property - def extract(self) -> AsyncExtractResourceWithRawResponse: - return AsyncExtractResourceWithRawResponse(self._v1.extract) - - @cached_property - def classify(self) -> AsyncClassifyResourceWithRawResponse: - return AsyncClassifyResourceWithRawResponse(self._v1.classify) - - -class V1ResourceWithStreamingResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - @cached_property - def extract(self) -> ExtractResourceWithStreamingResponse: - return ExtractResourceWithStreamingResponse(self._v1.extract) - - @cached_property - def classify(self) -> ClassifyResourceWithStreamingResponse: - return ClassifyResourceWithStreamingResponse(self._v1.classify) - - -class AsyncV1ResourceWithStreamingResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - @cached_property - def extract(self) -> AsyncExtractResourceWithStreamingResponse: - return AsyncExtractResourceWithStreamingResponse(self._v1.extract) - - @cached_property - def classify(self) -> AsyncClassifyResourceWithStreamingResponse: - return AsyncClassifyResourceWithStreamingResponse(self._v1.classify) diff --git a/src/docstrange/resources/api/v1/classify.py b/src/docstrange/resources/classify.py similarity index 81% rename from src/docstrange/resources/api/v1/classify.py rename to src/docstrange/resources/classify.py index fdd4c7a..5476a59 100644 --- a/src/docstrange/resources/api/v1/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,32 +2,22 @@ from __future__ import annotations -from typing import Mapping, cast - import httpx -from ...._types import ( - Body, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - not_given, -) -from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import ( +from ..types import classify_sync_params, classify_batch_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ...._base_client import make_request_options -from ....types.api.v1 import classify_sync_params, classify_batch_params -from ....types.api.v1.classify_sync_response import ClassifySyncResponse -from ....types.api.v1.classify_batch_response import ClassifyBatchResponse +from .._base_client import make_request_options +from ..types.classify_response import ClassifyResponse +from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -63,7 +53,7 @@ def batch( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ClassifyBatchResponse: + ) -> BatchClassifyResponse: """ Classify multiple documents asynchronously (max 50 files). @@ -83,25 +73,23 @@ def batch( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "files": files, - } - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/batch", - body=maybe_transform(body, classify_batch_params.ClassifyBatchParams), - files=extracted_files, + body=maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ClassifyBatchResponse, + cast_to=BatchClassifyResponse, ) def sync( @@ -115,7 +103,7 @@ def sync( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ClassifySyncResponse: + ) -> ClassifyResponse: """ Classify a single document into predefined categories synchronously. @@ -137,25 +125,23 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ClassifySyncResponse, + cast_to=ClassifyResponse, ) @@ -190,7 +176,7 @@ async def batch( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ClassifyBatchResponse: + ) -> BatchClassifyResponse: """ Classify multiple documents asynchronously (max 50 files). @@ -210,25 +196,23 @@ async def batch( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "files": files, - } - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/batch", - body=await async_maybe_transform(body, classify_batch_params.ClassifyBatchParams), - files=extracted_files, + body=await async_maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ClassifyBatchResponse, + cast_to=BatchClassifyResponse, ) async def sync( @@ -242,7 +226,7 @@ async def sync( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ClassifySyncResponse: + ) -> ClassifyResponse: """ Classify a single document into predefined categories synchronously. @@ -264,25 +248,23 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=await async_maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ClassifySyncResponse, + cast_to=ClassifyResponse, ) diff --git a/src/docstrange/resources/api/v1/extract/__init__.py b/src/docstrange/resources/extract/__init__.py similarity index 100% rename from src/docstrange/resources/api/v1/extract/__init__.py rename to src/docstrange/resources/extract/__init__.py diff --git a/src/docstrange/resources/api/v1/extract/extract.py b/src/docstrange/resources/extract/extract.py similarity index 89% rename from src/docstrange/resources/api/v1/extract/extract.py rename to src/docstrange/resources/extract/extract.py index 94ced26..bed3b08 100644 --- a/src/docstrange/resources/api/v1/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing import Mapping, cast from typing_extensions import Literal import httpx +from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params from .results import ( ResultsResource, AsyncResultsResource, @@ -15,7 +15,7 @@ ResultsResourceWithStreamingResponse, AsyncResultsResourceWithStreamingResponse, ) -from ....._types import ( +from ..._types import ( Body, Omit, Query, @@ -26,21 +26,20 @@ omit, not_given, ) -from ....._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from ....._compat import cached_property -from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import ( +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ....._streaming import Stream, AsyncStream -from ....._base_client import make_request_options -from .....types.api.v1 import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params -from .....types.api.v1.extract_response import ExtractResponse -from .....types.api.v1.extract_batch_response import ExtractBatchResponse -from .....types.api.v1.extract_stream_response import ExtractStreamResponse +from ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.extract_response import ExtractResponse +from ...types.batch_extract_response import BatchExtractResponse +from ...types.extract_stream_response import ExtractStreamResponse __all__ = ["ExtractResource", "AsyncExtractResource"] @@ -166,7 +165,7 @@ def batch( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractBatchResponse: + ) -> BatchExtractResponse: """Process multiple files asynchronously (max 50 files). All files share the same @@ -185,30 +184,28 @@ def batch( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/extract/batch", - body=maybe_transform(body, extract_batch_params.ExtractBatchParams), - files=extracted_files, + body=maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExtractBatchResponse, + cast_to=BatchExtractResponse, ) def stream( @@ -277,29 +274,27 @@ def stream( timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - body = deepcopy_minimal( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/extract/stream", - body=maybe_transform(body, extract_stream_params.ExtractStreamParams), - files=files, + body=maybe_transform( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -511,7 +506,7 @@ async def batch( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractBatchResponse: + ) -> BatchExtractResponse: """Process multiple files asynchronously (max 50 files). All files share the same @@ -530,30 +525,28 @@ async def batch( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/extract/batch", - body=await async_maybe_transform(body, extract_batch_params.ExtractBatchParams), - files=extracted_files, + body=await async_maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExtractBatchResponse, + cast_to=BatchExtractResponse, ) async def stream( @@ -622,29 +615,27 @@ async def stream( timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - body = deepcopy_minimal( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/extract/stream", - body=await async_maybe_transform(body, extract_stream_params.ExtractStreamParams), - files=files, + body=await async_maybe_transform( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/docstrange/resources/api/v1/extract/results.py b/src/docstrange/resources/extract/results.py similarity index 93% rename from src/docstrange/resources/api/v1/extract/results.py rename to src/docstrange/resources/extract/results.py index 51832fe..7f92467 100644 --- a/src/docstrange/resources/api/v1/extract/results.py +++ b/src/docstrange/resources/extract/results.py @@ -6,20 +6,20 @@ import httpx -from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ....._utils import maybe_transform, async_maybe_transform -from ....._compat import cached_property -from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import ( +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ....._base_client import make_request_options -from .....types.api.v1.extract import result_list_params, result_retrieve_params -from .....types.api.v1.extract_response import ExtractResponse -from .....types.api.v1.extract.result_list_response import ResultListResponse +from ..._base_client import make_request_options +from ...types.extract import result_list_params, result_retrieve_params +from ...types.extract_response import ExtractResponse +from ...types.extract.extraction_list_response import ExtractionListResponse __all__ = ["ResultsResource", "AsyncResultsResource"] @@ -100,7 +100,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResultListResponse: + ) -> ExtractionListResponse: """ List all extraction jobs for the authenticated user (paginated). @@ -130,7 +130,7 @@ def list( result_list_params.ResultListParams, ), ), - cast_to=ResultListResponse, + cast_to=ExtractionListResponse, ) @@ -210,7 +210,7 @@ async def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResultListResponse: + ) -> ExtractionListResponse: """ List all extraction jobs for the authenticated user (paginated). @@ -240,7 +240,7 @@ async def list( result_list_params.ResultListParams, ), ), - cast_to=ResultListResponse, + cast_to=ExtractionListResponse, ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index fa4182c..a18a10a 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -2,4 +2,20 @@ from __future__ import annotations +from .extract_response import ExtractResponse as ExtractResponse +from .classify_response import ClassifyResponse as ClassifyResponse +from .extraction_result import ExtractionResult as ExtractionResult +from .extract_sync_params import ExtractSyncParams as ExtractSyncParams +from .extraction_metadata import ExtractionMetadata as ExtractionMetadata +from .page_classification import PageClassification as PageClassification +from .classify_sync_params import ClassifySyncParams as ClassifySyncParams +from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams +from .extract_batch_params import ExtractBatchParams as ExtractBatchParams +from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams +from .extract_stream_params import ExtractStreamParams as ExtractStreamParams +from .batch_extract_response import BatchExtractResponse as BatchExtractResponse +from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse +from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse +from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult +from .file_classification_result import FileClassificationResult as FileClassificationResult from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/api/__init__.py b/src/docstrange/types/api/__init__.py deleted file mode 100644 index f8ee8b1..0000000 --- a/src/docstrange/types/api/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations diff --git a/src/docstrange/types/api/v1/__init__.py b/src/docstrange/types/api/v1/__init__.py deleted file mode 100644 index 45ae7db..0000000 --- a/src/docstrange/types/api/v1/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .extract_response import ExtractResponse as ExtractResponse -from .extract_sync_params import ExtractSyncParams as ExtractSyncParams -from .classify_sync_params import ClassifySyncParams as ClassifySyncParams -from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams -from .extract_batch_params import ExtractBatchParams as ExtractBatchParams -from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams -from .extract_stream_params import ExtractStreamParams as ExtractStreamParams -from .classify_sync_response import ClassifySyncResponse as ClassifySyncResponse -from .extract_batch_response import ExtractBatchResponse as ExtractBatchResponse -from .classify_batch_response import ClassifyBatchResponse as ClassifyBatchResponse -from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse -from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult -from .file_classification_result import FileClassificationResult as FileClassificationResult diff --git a/src/docstrange/types/api/v1/extraction_format_result.py b/src/docstrange/types/api/v1/extraction_format_result.py deleted file mode 100644 index a95327b..0000000 --- a/src/docstrange/types/api/v1/extraction_format_result.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional - -from ...._models import BaseModel - -__all__ = ["ExtractionFormatResult", "Metadata"] - - -class Metadata(BaseModel): - bounding_boxes: Optional[object] = None - """Element coordinates with page dimensions""" - - confidence_score: Optional[object] = None - """Confidence scores per field""" - - -class ExtractionFormatResult(BaseModel): - content: Union[str, object] - """Extracted content""" - - metadata: Optional[Metadata] = None diff --git a/src/docstrange/types/api/v1/classify_batch_response.py b/src/docstrange/types/batch_classify_response.py similarity index 76% rename from src/docstrange/types/api/v1/classify_batch_response.py rename to src/docstrange/types/batch_classify_response.py index d6a6b1b..a36e5a8 100644 --- a/src/docstrange/types/api/v1/classify_batch_response.py +++ b/src/docstrange/types/batch_classify_response.py @@ -2,13 +2,13 @@ from typing import List -from ...._models import BaseModel +from .._models import BaseModel from .file_classification_result import FileClassificationResult -__all__ = ["ClassifyBatchResponse"] +__all__ = ["BatchClassifyResponse"] -class ClassifyBatchResponse(BaseModel): +class BatchClassifyResponse(BaseModel): batch_id: str failed_files: int diff --git a/src/docstrange/types/api/v1/extract_batch_response.py b/src/docstrange/types/batch_extract_response.py similarity index 75% rename from src/docstrange/types/api/v1/extract_batch_response.py rename to src/docstrange/types/batch_extract_response.py index 2e0bfd8..4e73773 100644 --- a/src/docstrange/types/api/v1/extract_batch_response.py +++ b/src/docstrange/types/batch_extract_response.py @@ -2,13 +2,13 @@ from typing import List -from ...._models import BaseModel +from .._models import BaseModel from .extract_response import ExtractResponse -__all__ = ["ExtractBatchResponse"] +__all__ = ["BatchExtractResponse"] -class ExtractBatchResponse(BaseModel): +class BatchExtractResponse(BaseModel): accepted_files: int batch_id: str diff --git a/src/docstrange/types/api/v1/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py similarity index 89% rename from src/docstrange/types/api/v1/classify_batch_params.py rename to src/docstrange/types/classify_batch_params.py index 25f2754..5cac9e3 100644 --- a/src/docstrange/types/api/v1/classify_batch_params.py +++ b/src/docstrange/types/classify_batch_params.py @@ -4,7 +4,7 @@ from typing_extensions import Required, TypedDict -from ...._types import FileTypes, SequenceNotStr +from .._types import FileTypes, SequenceNotStr __all__ = ["ClassifyBatchParams"] diff --git a/src/docstrange/types/api/v1/classify_sync_response.py b/src/docstrange/types/classify_response.py similarity index 71% rename from src/docstrange/types/api/v1/classify_sync_response.py rename to src/docstrange/types/classify_response.py index 67383a7..fdebbb0 100644 --- a/src/docstrange/types/api/v1/classify_sync_response.py +++ b/src/docstrange/types/classify_response.py @@ -2,13 +2,13 @@ from typing import Optional -from ...._models import BaseModel +from .._models import BaseModel from .file_classification_result import FileClassificationResult -__all__ = ["ClassifySyncResponse"] +__all__ = ["ClassifyResponse"] -class ClassifySyncResponse(BaseModel): +class ClassifyResponse(BaseModel): message: str success: bool diff --git a/src/docstrange/types/api/v1/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py similarity index 93% rename from src/docstrange/types/api/v1/classify_sync_params.py rename to src/docstrange/types/classify_sync_params.py index 7b2be33..772427d 100644 --- a/src/docstrange/types/api/v1/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -4,7 +4,7 @@ from typing_extensions import Required, TypedDict -from ...._types import FileTypes +from .._types import FileTypes __all__ = ["ClassifySyncParams"] diff --git a/src/docstrange/types/api/v1/extract/__init__.py b/src/docstrange/types/extract/__init__.py similarity index 64% rename from src/docstrange/types/api/v1/extract/__init__.py rename to src/docstrange/types/extract/__init__.py index 07fc82d..387a007 100644 --- a/src/docstrange/types/api/v1/extract/__init__.py +++ b/src/docstrange/types/extract/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations +from .pagination_info import PaginationInfo as PaginationInfo from .result_list_params import ResultListParams as ResultListParams -from .result_list_response import ResultListResponse as ResultListResponse from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams +from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py new file mode 100644 index 0000000..26a12e7 --- /dev/null +++ b/src/docstrange/types/extract/extraction_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .pagination_info import PaginationInfo +from ..extract_response import ExtractResponse + +__all__ = ["ExtractionListResponse"] + + +class ExtractionListResponse(BaseModel): + pagination: PaginationInfo + + results: List[ExtractResponse] + + success: bool diff --git a/src/docstrange/types/api/v1/extract/result_list_response.py b/src/docstrange/types/extract/pagination_info.py similarity index 50% rename from src/docstrange/types/api/v1/extract/result_list_response.py rename to src/docstrange/types/extract/pagination_info.py index 86bfdeb..7a678a9 100644 --- a/src/docstrange/types/api/v1/extract/result_list_response.py +++ b/src/docstrange/types/extract/pagination_info.py @@ -1,14 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Optional -from ....._models import BaseModel -from ..extract_response import ExtractResponse +from ..._models import BaseModel -__all__ = ["ResultListResponse", "Pagination"] +__all__ = ["PaginationInfo"] -class Pagination(BaseModel): +class PaginationInfo(BaseModel): has_next: Optional[bool] = None has_previous: Optional[bool] = None @@ -20,11 +19,3 @@ class Pagination(BaseModel): total_count: Optional[int] = None total_pages: Optional[int] = None - - -class ResultListResponse(BaseModel): - pagination: Pagination - - results: List[ExtractResponse] - - success: bool diff --git a/src/docstrange/types/api/v1/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py similarity index 100% rename from src/docstrange/types/api/v1/extract/result_list_params.py rename to src/docstrange/types/extract/result_list_params.py diff --git a/src/docstrange/types/api/v1/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py similarity index 100% rename from src/docstrange/types/api/v1/extract/result_retrieve_params.py rename to src/docstrange/types/extract/result_retrieve_params.py diff --git a/src/docstrange/types/api/v1/extract_async_params.py b/src/docstrange/types/extract_async_params.py similarity index 97% rename from src/docstrange/types/api/v1/extract_async_params.py rename to src/docstrange/types/extract_async_params.py index cb154f4..d638b09 100644 --- a/src/docstrange/types/api/v1/extract_async_params.py +++ b/src/docstrange/types/extract_async_params.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -from ...._types import FileTypes +from .._types import FileTypes __all__ = ["ExtractAsyncParams"] diff --git a/src/docstrange/types/api/v1/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py similarity index 92% rename from src/docstrange/types/api/v1/extract_batch_params.py rename to src/docstrange/types/extract_batch_params.py index 566c7dc..bdc7b29 100644 --- a/src/docstrange/types/api/v1/extract_batch_params.py +++ b/src/docstrange/types/extract_batch_params.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -from ...._types import FileTypes, SequenceNotStr +from .._types import FileTypes, SequenceNotStr __all__ = ["ExtractBatchParams"] diff --git a/src/docstrange/types/api/v1/extract_response.py b/src/docstrange/types/extract_response.py similarity index 57% rename from src/docstrange/types/api/v1/extract_response.py rename to src/docstrange/types/extract_response.py index 875b357..9d93ead 100644 --- a/src/docstrange/types/api/v1/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -4,24 +4,10 @@ from datetime import datetime from typing_extensions import Literal -from pydantic import Field as FieldInfo +from .._models import BaseModel +from .extraction_result import ExtractionResult -from ...._models import BaseModel -from .extraction_format_result import ExtractionFormatResult - -__all__ = ["ExtractResponse", "Result"] - - -class Result(BaseModel): - """Results by format (only requested formats populated)""" - - csv: Optional[ExtractionFormatResult] = None - - html: Optional[ExtractionFormatResult] = None - - json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) - - markdown: Optional[ExtractionFormatResult] = None +__all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): @@ -48,5 +34,5 @@ class ExtractResponse(BaseModel): processing_time: Optional[float] = None """Time in seconds""" - result: Optional[Result] = None + result: Optional[ExtractionResult] = None """Results by format (only requested formats populated)""" diff --git a/src/docstrange/types/api/v1/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py similarity index 97% rename from src/docstrange/types/api/v1/extract_stream_params.py rename to src/docstrange/types/extract_stream_params.py index 3d5a388..2a8e9e3 100644 --- a/src/docstrange/types/api/v1/extract_stream_params.py +++ b/src/docstrange/types/extract_stream_params.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -from ...._types import FileTypes +from .._types import FileTypes __all__ = ["ExtractStreamParams"] diff --git a/src/docstrange/types/api/v1/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py similarity index 100% rename from src/docstrange/types/api/v1/extract_stream_response.py rename to src/docstrange/types/extract_stream_response.py diff --git a/src/docstrange/types/api/v1/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py similarity index 97% rename from src/docstrange/types/api/v1/extract_sync_params.py rename to src/docstrange/types/extract_sync_params.py index 50cc59a..e3c4bea 100644 --- a/src/docstrange/types/api/v1/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -from ...._types import FileTypes +from .._types import FileTypes __all__ = ["ExtractSyncParams"] diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py new file mode 100644 index 0000000..579500c --- /dev/null +++ b/src/docstrange/types/extraction_format_result.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional + +from .._models import BaseModel +from .extraction_metadata import ExtractionMetadata + +__all__ = ["ExtractionFormatResult"] + + +class ExtractionFormatResult(BaseModel): + content: Union[str, object] + """Extracted content""" + + metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py new file mode 100644 index 0000000..9792ba0 --- /dev/null +++ b/src/docstrange/types/extraction_metadata.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExtractionMetadata"] + + +class ExtractionMetadata(BaseModel): + bounding_boxes: Optional[object] = None + """Element coordinates with page dimensions""" + + confidence_score: Optional[object] = None + """Confidence scores per field""" diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py new file mode 100644 index 0000000..57b2928 --- /dev/null +++ b/src/docstrange/types/extraction_result.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .extraction_format_result import ExtractionFormatResult + +__all__ = ["ExtractionResult"] + + +class ExtractionResult(BaseModel): + """Results by format (only requested formats populated)""" + + csv: Optional[ExtractionFormatResult] = None + + html: Optional[ExtractionFormatResult] = None + + json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) + + markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py new file mode 100644 index 0000000..397d924 --- /dev/null +++ b/src/docstrange/types/file_classification_result.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .page_classification import PageClassification + +__all__ = ["FileClassificationResult"] + + +class FileClassificationResult(BaseModel): + filename: str + + pages: List[PageClassification] + + total_pages: int + + error: Optional[str] = None + + processing_time: Optional[float] = None + """Time in seconds""" + + record_id: Optional[str] = None + """Record ID for polling results""" diff --git a/src/docstrange/types/api/v1/file_classification_result.py b/src/docstrange/types/page_classification.py similarity index 56% rename from src/docstrange/types/api/v1/file_classification_result.py rename to src/docstrange/types/page_classification.py index 2ec6b94..2099524 100644 --- a/src/docstrange/types/api/v1/file_classification_result.py +++ b/src/docstrange/types/page_classification.py @@ -1,13 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Optional -from ...._models import BaseModel +from .._models import BaseModel -__all__ = ["FileClassificationResult", "Page"] +__all__ = ["PageClassification"] -class Page(BaseModel): +class PageClassification(BaseModel): category: str """Matched category name or 'Other' if no match""" @@ -25,19 +25,3 @@ class Page(BaseModel): 'Invoice_Amazon_Electronics'). Always provided - contains a more detailed classification than the category. """ - - -class FileClassificationResult(BaseModel): - filename: str - - pages: List[Page] - - total_pages: int - - error: Optional[str] = None - - processing_time: Optional[float] = None - """Time in seconds""" - - record_id: Optional[str] = None - """Record ID for polling results""" diff --git a/tests/api_resources/api/v1/__init__.py b/tests/api_resources/api/v1/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/api/v1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/api/v1/extract/__init__.py b/tests/api_resources/api/v1/extract/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/api/v1/extract/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/api/__init__.py b/tests/api_resources/extract/__init__.py similarity index 100% rename from tests/api_resources/api/__init__.py rename to tests/api_resources/extract/__init__.py diff --git a/tests/api_resources/api/v1/extract/test_results.py b/tests/api_resources/extract/test_results.py similarity index 76% rename from tests/api_resources/api/v1/extract/test_results.py rename to tests/api_resources/extract/test_results.py index 5c842cc..372de57 100644 --- a/tests/api_resources/api/v1/extract/test_results.py +++ b/tests/api_resources/extract/test_results.py @@ -9,8 +9,8 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types.api.v1 import ExtractResponse -from docstrange.types.api.v1.extract import ResultListResponse +from docstrange.types import ExtractResponse +from docstrange.types.extract import ExtractionListResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -21,7 +21,7 @@ class TestResults: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: Docstrange) -> None: - result = client.api.v1.extract.results.retrieve( + result = client.extract.results.retrieve( record_id="record_id", ) assert_matches_type(ExtractResponse, result, path=["response"]) @@ -29,7 +29,7 @@ def test_method_retrieve(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: - result = client.api.v1.extract.results.retrieve( + result = client.extract.results.retrieve( record_id="record_id", include_content=True, ) @@ -38,7 +38,7 @@ def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: Docstrange) -> None: - response = client.api.v1.extract.results.with_raw_response.retrieve( + response = client.extract.results.with_raw_response.retrieve( record_id="record_id", ) @@ -50,7 +50,7 @@ def test_raw_response_retrieve(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: Docstrange) -> None: - with client.api.v1.extract.results.with_streaming_response.retrieve( + with client.extract.results.with_streaming_response.retrieve( record_id="record_id", ) as response: assert not response.is_closed @@ -65,46 +65,46 @@ def test_streaming_response_retrieve(self, client: Docstrange) -> None: @parametrize def test_path_params_retrieve(self, client: Docstrange) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - client.api.v1.extract.results.with_raw_response.retrieve( + client.extract.results.with_raw_response.retrieve( record_id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Docstrange) -> None: - result = client.api.v1.extract.results.list() - assert_matches_type(ResultListResponse, result, path=["response"]) + result = client.extract.results.list() + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: Docstrange) -> None: - result = client.api.v1.extract.results.list( + result = client.extract.results.list( page=1, page_size=1, sort_by="created_at", sort_order="asc", ) - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Docstrange) -> None: - response = client.api.v1.extract.results.with_raw_response.list() + response = client.extract.results.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = response.parse() - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: Docstrange) -> None: - with client.api.v1.extract.results.with_streaming_response.list() as response: + with client.extract.results.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = response.parse() - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) assert cast(Any, response.is_closed) is True @@ -117,7 +117,7 @@ class TestAsyncResults: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: - result = await async_client.api.v1.extract.results.retrieve( + result = await async_client.extract.results.retrieve( record_id="record_id", ) assert_matches_type(ExtractResponse, result, path=["response"]) @@ -125,7 +125,7 @@ async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.api.v1.extract.results.retrieve( + result = await async_client.extract.results.retrieve( record_id="record_id", include_content=True, ) @@ -134,7 +134,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstran @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.results.with_raw_response.retrieve( + response = await async_client.extract.results.with_raw_response.retrieve( record_id="record_id", ) @@ -146,7 +146,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> Non @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.results.with_streaming_response.retrieve( + async with async_client.extract.results.with_streaming_response.retrieve( record_id="record_id", ) as response: assert not response.is_closed @@ -161,45 +161,45 @@ async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) @parametrize async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - await async_client.api.v1.extract.results.with_raw_response.retrieve( + await async_client.extract.results.with_raw_response.retrieve( record_id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncDocstrange) -> None: - result = await async_client.api.v1.extract.results.list() - assert_matches_type(ResultListResponse, result, path=["response"]) + result = await async_client.extract.results.list() + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.api.v1.extract.results.list( + result = await async_client.extract.results.list( page=1, page_size=1, sort_by="created_at", sort_order="asc", ) - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.results.with_raw_response.list() + response = await async_client.extract.results.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = await response.parse() - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.results.with_streaming_response.list() as response: + async with async_client.extract.results.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = await response.parse() - assert_matches_type(ResultListResponse, result, path=["response"]) + assert_matches_type(ExtractionListResponse, result, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/api/v1/test_classify.py b/tests/api_resources/test_classify.py similarity index 78% rename from tests/api_resources/api/v1/test_classify.py rename to tests/api_resources/test_classify.py index c8a9b4c..b4377f6 100644 --- a/tests/api_resources/api/v1/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,10 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types.api.v1 import ( - ClassifySyncResponse, - ClassifyBatchResponse, -) +from docstrange.types import ClassifyResponse, BatchClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,16 +20,16 @@ class TestClassify: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_batch(self, client: Docstrange) -> None: - classify = client.api.v1.classify.batch( + classify = client.classify.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.api.v1.classify.with_raw_response.batch( + response = client.classify.with_raw_response.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) @@ -40,12 +37,12 @@ def test_raw_response_batch(self, client: Docstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = response.parse() - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.api.v1.classify.with_streaming_response.batch( + with client.classify.with_streaming_response.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) as response: @@ -53,23 +50,23 @@ def test_streaming_response_batch(self, client: Docstrange) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = response.parse() - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: - classify = client.api.v1.classify.sync( + classify = client.classify.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: - response = client.api.v1.classify.with_raw_response.sync( + response = client.classify.with_raw_response.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -77,12 +74,12 @@ def test_raw_response_sync(self, client: Docstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = response.parse() - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: - with client.api.v1.classify.with_streaming_response.sync( + with client.classify.with_streaming_response.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: @@ -90,7 +87,7 @@ def test_streaming_response_sync(self, client: Docstrange) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = response.parse() - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) assert cast(Any, response.is_closed) is True @@ -103,16 +100,16 @@ class TestAsyncClassify: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.api.v1.classify.batch( + classify = await async_client.classify.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.classify.with_raw_response.batch( + response = await async_client.classify.with_raw_response.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) @@ -120,12 +117,12 @@ async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = await response.parse() - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.classify.with_streaming_response.batch( + async with async_client.classify.with_streaming_response.batch( categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', files=[b"raw file contents"], ) as response: @@ -133,23 +130,23 @@ async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = await response.parse() - assert_matches_type(ClassifyBatchResponse, classify, path=["response"]) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.api.v1.classify.sync( + classify = await async_client.classify.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.classify.with_raw_response.sync( + response = await async_client.classify.with_raw_response.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -157,12 +154,12 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = await response.parse() - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.classify.with_streaming_response.sync( + async with async_client.classify.with_streaming_response.sync( categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: @@ -170,6 +167,6 @@ async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" classify = await response.parse() - assert_matches_type(ClassifySyncResponse, classify, path=["response"]) + assert_matches_type(ClassifyResponse, classify, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/api/v1/test_extract.py b/tests/api_resources/test_extract.py similarity index 84% rename from tests/api_resources/api/v1/test_extract.py rename to tests/api_resources/test_extract.py index a1a05ff..ffdac33 100644 --- a/tests/api_resources/api/v1/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,9 +9,9 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types.api.v1 import ( +from docstrange.types import ( ExtractResponse, - ExtractBatchResponse, + BatchExtractResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,7 +23,7 @@ class TestExtract: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_async(self, client: Docstrange) -> None: - extract = client.api.v1.extract.async_( + extract = client.extract.async_( output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -31,7 +31,7 @@ def test_method_async(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_async_with_all_params(self, client: Docstrange) -> None: - extract = client.api.v1.extract.async_( + extract = client.extract.async_( output_format="markdown", csv_options="", custom_instructions="", @@ -47,7 +47,7 @@ def test_method_async_with_all_params(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_async(self, client: Docstrange) -> None: - response = client.api.v1.extract.with_raw_response.async_( + response = client.extract.with_raw_response.async_( output_format="markdown", ) @@ -59,7 +59,7 @@ def test_raw_response_async(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_async(self, client: Docstrange) -> None: - with client.api.v1.extract.with_streaming_response.async_( + with client.extract.with_streaming_response.async_( output_format="markdown", ) as response: assert not response.is_closed @@ -73,16 +73,16 @@ def test_streaming_response_async(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_batch(self, client: Docstrange) -> None: - extract = client.api.v1.extract.batch( + extract = client.extract.batch( files=[b"raw file contents"], output_format="markdown", ) - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_batch_with_all_params(self, client: Docstrange) -> None: - extract = client.api.v1.extract.batch( + extract = client.extract.batch( files=[b"raw file contents"], output_format="markdown", csv_options="csv_options", @@ -91,12 +91,12 @@ def test_method_batch_with_all_params(self, client: Docstrange) -> None: json_options="json_options", prompt_mode="append", ) - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.api.v1.extract.with_raw_response.batch( + response = client.extract.with_raw_response.batch( files=[b"raw file contents"], output_format="markdown", ) @@ -104,12 +104,12 @@ def test_raw_response_batch(self, client: Docstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" extract = response.parse() - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.api.v1.extract.with_streaming_response.batch( + with client.extract.with_streaming_response.batch( files=[b"raw file contents"], output_format="markdown", ) as response: @@ -117,14 +117,14 @@ def test_streaming_response_batch(self, client: Docstrange) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" extract = response.parse() - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_method_stream(self, client: Docstrange) -> None: - extract_stream = client.api.v1.extract.stream( + extract_stream = client.extract.stream( output_format="markdown", ) extract_stream.response.close() @@ -132,7 +132,7 @@ def test_method_stream(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_method_stream_with_all_params(self, client: Docstrange) -> None: - extract_stream = client.api.v1.extract.stream( + extract_stream = client.extract.stream( output_format="markdown", csv_options="", custom_instructions="", @@ -149,7 +149,7 @@ def test_method_stream_with_all_params(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_raw_response_stream(self, client: Docstrange) -> None: - response = client.api.v1.extract.with_raw_response.stream( + response = client.extract.with_raw_response.stream( output_format="markdown", ) @@ -160,7 +160,7 @@ def test_raw_response_stream(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_streaming_response_stream(self, client: Docstrange) -> None: - with client.api.v1.extract.with_streaming_response.stream( + with client.extract.with_streaming_response.stream( output_format="markdown", ) as response: assert not response.is_closed @@ -174,7 +174,7 @@ def test_streaming_response_stream(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: - extract = client.api.v1.extract.sync( + extract = client.extract.sync( output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -182,7 +182,7 @@ def test_method_sync(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync_with_all_params(self, client: Docstrange) -> None: - extract = client.api.v1.extract.sync( + extract = client.extract.sync( output_format="markdown", csv_options="", custom_instructions="", @@ -198,7 +198,7 @@ def test_method_sync_with_all_params(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: - response = client.api.v1.extract.with_raw_response.sync( + response = client.extract.with_raw_response.sync( output_format="markdown", ) @@ -210,7 +210,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: - with client.api.v1.extract.with_streaming_response.sync( + with client.extract.with_streaming_response.sync( output_format="markdown", ) as response: assert not response.is_closed @@ -230,7 +230,7 @@ class TestAsyncExtract: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_async(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.async_( + extract = await async_client.extract.async_( output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -238,7 +238,7 @@ async def test_method_async(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.async_( + extract = await async_client.extract.async_( output_format="markdown", csv_options="", custom_instructions="", @@ -254,7 +254,7 @@ async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.with_raw_response.async_( + response = await async_client.extract.with_raw_response.async_( output_format="markdown", ) @@ -266,7 +266,7 @@ async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.with_streaming_response.async_( + async with async_client.extract.with_streaming_response.async_( output_format="markdown", ) as response: assert not response.is_closed @@ -280,16 +280,16 @@ async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.batch( + extract = await async_client.extract.batch( files=[b"raw file contents"], output_format="markdown", ) - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.batch( + extract = await async_client.extract.batch( files=[b"raw file contents"], output_format="markdown", csv_options="csv_options", @@ -298,12 +298,12 @@ async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) json_options="json_options", prompt_mode="append", ) - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.with_raw_response.batch( + response = await async_client.extract.with_raw_response.batch( files=[b"raw file contents"], output_format="markdown", ) @@ -311,12 +311,12 @@ async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" extract = await response.parse() - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.with_streaming_response.batch( + async with async_client.extract.with_streaming_response.batch( files=[b"raw file contents"], output_format="markdown", ) as response: @@ -324,14 +324,14 @@ async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> assert response.http_request.headers.get("X-Stainless-Lang") == "python" extract = await response.parse() - assert_matches_type(ExtractBatchResponse, extract, path=["response"]) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_method_stream(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.api.v1.extract.stream( + extract_stream = await async_client.extract.stream( output_format="markdown", ) await extract_stream.response.aclose() @@ -339,7 +339,7 @@ async def test_method_stream(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.api.v1.extract.stream( + extract_stream = await async_client.extract.stream( output_format="markdown", csv_options="", custom_instructions="", @@ -356,7 +356,7 @@ async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.with_raw_response.stream( + response = await async_client.extract.with_raw_response.stream( output_format="markdown", ) @@ -367,7 +367,7 @@ async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.with_streaming_response.stream( + async with async_client.extract.with_streaming_response.stream( output_format="markdown", ) as response: assert not response.is_closed @@ -381,7 +381,7 @@ async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.sync( + extract = await async_client.extract.sync( output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -389,7 +389,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.api.v1.extract.sync( + extract = await async_client.extract.sync( output_format="markdown", csv_options="", custom_instructions="", @@ -405,7 +405,7 @@ async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: - response = await async_client.api.v1.extract.with_raw_response.sync( + response = await async_client.extract.with_raw_response.sync( output_format="markdown", ) @@ -417,7 +417,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: - async with async_client.api.v1.extract.with_streaming_response.sync( + async with async_client.extract.with_streaming_response.sync( output_format="markdown", ) as response: assert not response.is_closed diff --git a/tests/test_client.py b/tests/test_client.py index 89f274a..3d576f6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,7 +862,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.api.v1.extract.with_streaming_response.sync(output_format="markdown").__enter__() + client.extract.with_streaming_response.sync(output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @@ -872,7 +872,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.api.v1.extract.with_streaming_response.sync(output_format="markdown").__enter__() + client.extract.with_streaming_response.sync(output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -901,7 +901,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.api.v1.extract.with_raw_response.sync(output_format="markdown") + response = client.extract.with_raw_response.sync(output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -925,7 +925,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.api.v1.extract.with_raw_response.sync( + response = client.extract.with_raw_response.sync( output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) @@ -950,7 +950,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.api.v1.extract.with_raw_response.sync( + response = client.extract.with_raw_response.sync( output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) @@ -1768,7 +1768,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.api.v1.extract.with_streaming_response.sync(output_format="markdown").__aenter__() + await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() assert _get_open_connections(async_client) == 0 @@ -1780,7 +1780,7 @@ async def test_retrying_status_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.api.v1.extract.with_streaming_response.sync(output_format="markdown").__aenter__() + await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1809,7 +1809,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.api.v1.extract.with_raw_response.sync(output_format="markdown") + response = await client.extract.with_raw_response.sync(output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1833,7 +1833,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.api.v1.extract.with_raw_response.sync( + response = await client.extract.with_raw_response.sync( output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) @@ -1858,7 +1858,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.api.v1.extract.with_raw_response.sync( + response = await client.extract.with_raw_response.sync( output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) From 1ccd49469cb3b08b7df945cb54737572dd04097e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 07:10:35 +0000 Subject: [PATCH 07/26] Add pagination config, fix readme params --- .stats.yml | 2 +- src/docstrange/pagination.py | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/docstrange/pagination.py diff --git a/.stats.yml b/.stats.yml index 1774d1c..d0e1c4a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 5095aef18b1e8c018bc58e39d6f4b23d +config_hash: 618498b0a12e1535b716981b83e1f760 diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py new file mode 100644 index 0000000..9c8dc87 --- /dev/null +++ b/src/docstrange/pagination.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional, cast +from typing_extensions import override + +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["SyncPageNumberPagination", "AsyncPageNumberPagination"] + +_T = TypeVar("_T") + + +class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + total_count: Optional[int] = None + has_next: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + return PageInfo(params={"page": last_page + 1}) + + +class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + total_count: Optional[int] = None + has_next: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + return PageInfo(params={"page": last_page + 1}) From 4fc3af695ffd8664a21c22edb00a4c23cfdf7186 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 07:12:51 +0000 Subject: [PATCH 08/26] Fix pagination schema to match nested response structure --- .stats.yml | 2 +- README.md | 61 +++++++++++++++++++++ api.md | 2 +- src/docstrange/pagination.py | 45 ++++++++++----- src/docstrange/resources/extract/results.py | 22 ++++---- tests/api_resources/extract/test_results.py | 18 +++--- 6 files changed, 116 insertions(+), 34 deletions(-) diff --git a/.stats.yml b/.stats.yml index d0e1c4a..e1a1b53 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 618498b0a12e1535b716981b83e1f760 +config_hash: de4c18021060af5ebd13aa207675d379 diff --git a/README.md b/README.md index 9532911..917f116 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,67 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Docstrange API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from docstrange import Docstrange + +client = Docstrange() + +all_results = [] +# Automatically fetches more pages as needed. +for result in client.extract.results.list(): + # Do something with result here + all_results.append(result) +print(all_results) +``` + +Or, asynchronously: + +```python +import asyncio +from docstrange import AsyncDocstrange + +client = AsyncDocstrange() + + +async def main() -> None: + all_results = [] + # Iterate through items across all pages, issuing requests as needed. + async for result in client.extract.results.list(): + all_results.append(result) + print(all_results) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.extract.results.list() +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.results)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.extract.results.list() +for result in first_page.results: + print(result.record_id) + +# Remove `await` for non-async usage. +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 2f2f496..7ccbce8 100644 --- a/api.md +++ b/api.md @@ -34,7 +34,7 @@ from docstrange.types.extract import ExtractionListResponse, PaginationInfo Methods: - client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.extract.results.list(\*\*params) -> ExtractionListResponse +- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] # Classify diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py index 9c8dc87..e926095 100644 --- a/src/docstrange/pagination.py +++ b/src/docstrange/pagination.py @@ -3,46 +3,65 @@ from typing import List, Generic, TypeVar, Optional, cast from typing_extensions import override +from ._models import BaseModel from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage -__all__ = ["SyncPageNumberPagination", "AsyncPageNumberPagination"] +__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] _T = TypeVar("_T") -class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - items: List[_T] - total_count: Optional[int] = None +class PageNumberPaginationPagination(BaseModel): has_next: Optional[bool] = None + total_count: Optional[int] = None + + +class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + @override def _get_page_items(self) -> List[_T]: - items = self.items - if not items: + results = self.results + if not results: return [] - return items + return results @override def next_page_info(self) -> Optional[PageInfo]: last_page = cast("int | None", self._options.params.get("page")) or 1 + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + return PageInfo(params={"page": last_page + 1}) class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - items: List[_T] - total_count: Optional[int] = None - has_next: Optional[bool] = None + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None @override def _get_page_items(self) -> List[_T]: - items = self.items - if not items: + results = self.results + if not results: return [] - return items + return results @override def next_page_info(self) -> Optional[PageInfo]: last_page = cast("int | None", self._options.params.get("page")) or 1 + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py index 7f92467..1533ac2 100644 --- a/src/docstrange/resources/extract/results.py +++ b/src/docstrange/resources/extract/results.py @@ -16,10 +16,10 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options +from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination +from ..._base_client import AsyncPaginator, make_request_options from ...types.extract import result_list_params, result_retrieve_params from ...types.extract_response import ExtractResponse -from ...types.extract.extraction_list_response import ExtractionListResponse __all__ = ["ResultsResource", "AsyncResultsResource"] @@ -100,7 +100,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractionListResponse: + ) -> SyncPageNumberPagination[ExtractResponse]: """ List all extraction jobs for the authenticated user (paginated). @@ -113,8 +113,9 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - return self._get( + return self._get_api_list( "/api/v1/extract/results", + page=SyncPageNumberPagination[ExtractResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -130,7 +131,7 @@ def list( result_list_params.ResultListParams, ), ), - cast_to=ExtractionListResponse, + model=ExtractResponse, ) @@ -196,7 +197,7 @@ async def retrieve( cast_to=ExtractResponse, ) - async def list( + def list( self, *, page: int | Omit = omit, @@ -210,7 +211,7 @@ async def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractionListResponse: + ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: """ List all extraction jobs for the authenticated user (paginated). @@ -223,14 +224,15 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ - return await self._get( + return self._get_api_list( "/api/v1/extract/results", + page=AsyncPageNumberPagination[ExtractResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( + query=maybe_transform( { "page": page, "page_size": page_size, @@ -240,7 +242,7 @@ async def list( result_list_params.ResultListParams, ), ), - cast_to=ExtractionListResponse, + model=ExtractResponse, ) diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py index 372de57..ee1d5f5 100644 --- a/tests/api_resources/extract/test_results.py +++ b/tests/api_resources/extract/test_results.py @@ -10,7 +10,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type from docstrange.types import ExtractResponse -from docstrange.types.extract import ExtractionListResponse +from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -73,7 +73,7 @@ def test_path_params_retrieve(self, client: Docstrange) -> None: @parametrize def test_method_list(self, client: Docstrange) -> None: result = client.extract.results.list() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -84,7 +84,7 @@ def test_method_list_with_all_params(self, client: Docstrange) -> None: sort_by="created_at", sort_order="asc", ) - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -94,7 +94,7 @@ def test_raw_response_list(self, client: Docstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = response.parse() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -104,7 +104,7 @@ def test_streaming_response_list(self, client: Docstrange) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = response.parse() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) assert cast(Any, response.is_closed) is True @@ -169,7 +169,7 @@ async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None @parametrize async def test_method_list(self, async_client: AsyncDocstrange) -> None: result = await async_client.extract.results.list() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -180,7 +180,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) sort_by="created_at", sort_order="asc", ) - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -190,7 +190,7 @@ async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = await response.parse() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -200,6 +200,6 @@ async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" result = await response.parse() - assert_matches_type(ExtractionListResponse, result, path=["response"]) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) assert cast(Any, response.is_closed) is True From 5b89e7feb42bbe9d637c4d60a021fde1291e9577 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 07:14:34 +0000 Subject: [PATCH 09/26] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index e1a1b53..bf2dc75 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: de4c18021060af5ebd13aa207675d379 +config_hash: 1baaa1d64ec6584ed1f597237a15f16d From 43a5e850d97e9bed4b1dd1ec1d07a9073b9db21d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:09:54 +0000 Subject: [PATCH 10/26] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bf2dc75..cf66929 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-bc5cce7d8bf43d1c05a2b9463a3ea8d64ab2fa909d074ddfc1a09665a61c3642.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 1baaa1d64ec6584ed1f597237a15f16d +config_hash: b3698eed9a1747fbb26a5f19a066b037 From e1b836c1338d0c34eeae2156a19ae1a9a68b9269 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:22:54 +0000 Subject: [PATCH 11/26] chore(internal): fix lint error on Python 3.14 --- src/docstrange/_utils/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docstrange/_utils/_compat.py b/src/docstrange/_utils/_compat.py index dd70323..2c70b29 100644 --- a/src/docstrange/_utils/_compat.py +++ b/src/docstrange/_utils/_compat.py @@ -26,7 +26,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool: else: import types - return tp is Union or tp is types.UnionType + return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] def is_typeddict(tp: Type[Any]) -> bool: From d88ea559f0d053b2bac7d05b139c31533e8d4843 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 20:24:51 +0000 Subject: [PATCH 12/26] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index cf66929..1cf30c8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-bc5cce7d8bf43d1c05a2b9463a3ea8d64ab2fa909d074ddfc1a09665a61c3642.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-f82f6b5e52eaf62639b7ae43348bbf66363cc95592391797a0ae297d3c7f738f.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: b3698eed9a1747fbb26a5f19a066b037 +config_hash: 52d207e1bd85f0522c9cae6ea8805784 From 3d12679a9c3ff3c58563bc17c15fab514dd3aa36 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:25:23 +0000 Subject: [PATCH 13/26] feat(api): api update --- .stats.yml | 8 +- README.md | 71 +- api.md | 52 +- src/docstrange/_client.py | 41 +- src/docstrange/pagination.py | 67 -- src/docstrange/resources/__init__.py | 14 - src/docstrange/resources/chat.py | 218 ----- src/docstrange/resources/classify.py | 175 +--- src/docstrange/resources/extract.py | 199 +++++ src/docstrange/resources/extract/__init__.py | 33 - src/docstrange/resources/extract/extract.py | 814 ------------------ src/docstrange/resources/extract/results.py | 294 ------- src/docstrange/types/__init__.py | 13 - .../types/batch_classify_response.py | 24 - .../types/batch_extract_response.py | 24 - .../types/chat_create_completion_params.py | 45 - src/docstrange/types/classify_batch_params.py | 17 - src/docstrange/types/classify_response.py | 5 - src/docstrange/types/classify_sync_params.py | 7 +- src/docstrange/types/extract/__init__.py | 8 - .../types/extract/extraction_list_response.py | 17 - .../types/extract/pagination_info.py | 21 - .../types/extract/result_list_params.py | 17 - .../types/extract/result_retrieve_params.py | 12 - src/docstrange/types/extract_async_params.py | 45 - src/docstrange/types/extract_batch_params.py | 27 - src/docstrange/types/extract_response.py | 25 +- src/docstrange/types/extract_stream_params.py | 47 - .../types/extract_stream_response.py | 7 - src/docstrange/types/extract_sync_params.py | 38 +- .../types/extraction_format_result.py | 15 - src/docstrange/types/extraction_metadata.py | 15 - src/docstrange/types/extraction_result.py | 22 - .../types/file_classification_result.py | 24 - src/docstrange/types/page_classification.py | 27 - tests/api_resources/extract/__init__.py | 1 - tests/api_resources/extract/test_results.py | 205 ----- tests/api_resources/test_chat.py | 183 ---- tests/api_resources/test_classify.py | 88 +- tests/api_resources/test_extract.py | 353 +------- tests/test_client.py | 28 +- 41 files changed, 291 insertions(+), 3055 deletions(-) delete mode 100644 src/docstrange/pagination.py delete mode 100644 src/docstrange/resources/chat.py create mode 100644 src/docstrange/resources/extract.py delete mode 100644 src/docstrange/resources/extract/__init__.py delete mode 100644 src/docstrange/resources/extract/extract.py delete mode 100644 src/docstrange/resources/extract/results.py delete mode 100644 src/docstrange/types/batch_classify_response.py delete mode 100644 src/docstrange/types/batch_extract_response.py delete mode 100644 src/docstrange/types/chat_create_completion_params.py delete mode 100644 src/docstrange/types/classify_batch_params.py delete mode 100644 src/docstrange/types/extract/__init__.py delete mode 100644 src/docstrange/types/extract/extraction_list_response.py delete mode 100644 src/docstrange/types/extract/pagination_info.py delete mode 100644 src/docstrange/types/extract/result_list_params.py delete mode 100644 src/docstrange/types/extract/result_retrieve_params.py delete mode 100644 src/docstrange/types/extract_async_params.py delete mode 100644 src/docstrange/types/extract_batch_params.py delete mode 100644 src/docstrange/types/extract_stream_params.py delete mode 100644 src/docstrange/types/extract_stream_response.py delete mode 100644 src/docstrange/types/extraction_format_result.py delete mode 100644 src/docstrange/types/extraction_metadata.py delete mode 100644 src/docstrange/types/extraction_result.py delete mode 100644 src/docstrange/types/file_classification_result.py delete mode 100644 src/docstrange/types/page_classification.py delete mode 100644 tests/api_resources/extract/__init__.py delete mode 100644 tests/api_resources/extract/test_results.py delete mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 1cf30c8..06a3238 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-f82f6b5e52eaf62639b7ae43348bbf66363cc95592391797a0ae297d3c7f738f.yml -openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 52d207e1bd85f0522c9cae6ea8805784 +configured_endpoints: 2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-00ea96039f73fd253b5042c3e5c1ae800ca6b3bbde585408d20a46f73ae87ecf.yml +openapi_spec_hash: b4046d95714426b15edb8ac003f904c6 +config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 diff --git a/README.md b/README.md index 917f116..622faec 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ client = Docstrange( ) extract_response = client.extract.sync( + file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -59,6 +60,7 @@ client = AsyncDocstrange( async def main() -> None: extract_response = await client.extract.sync( + file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -95,6 +97,7 @@ async def main() -> None: http_client=DefaultAioHttpClient(), ) as client: extract_response = await client.extract.sync( + file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -112,67 +115,6 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## Pagination - -List methods in the Docstrange API are paginated. - -This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: - -```python -from docstrange import Docstrange - -client = Docstrange() - -all_results = [] -# Automatically fetches more pages as needed. -for result in client.extract.results.list(): - # Do something with result here - all_results.append(result) -print(all_results) -``` - -Or, asynchronously: - -```python -import asyncio -from docstrange import AsyncDocstrange - -client = AsyncDocstrange() - - -async def main() -> None: - all_results = [] - # Iterate through items across all pages, issuing requests as needed. - async for result in client.extract.results.list(): - all_results.append(result) - print(all_results) - - -asyncio.run(main()) -``` - -Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: - -```python -first_page = await client.extract.results.list() -if first_page.has_next_page(): - print(f"will fetch next page using these details: {first_page.next_page_info()}") - next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.results)}") - -# Remove `await` for non-async usage. -``` - -Or just work directly with the returned data: - -```python -first_page = await client.extract.results.list() -for result in first_page.results: - print(result.record_id) - -# Remove `await` for non-async usage. -``` - ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. @@ -184,8 +126,8 @@ from docstrange import Docstrange client = Docstrange() client.extract.sync( - output_format="markdown", file=Path("/path/to/file"), + output_format="output_format", ) ``` @@ -208,6 +150,7 @@ client = Docstrange() try: client.extract.sync( + file=b"raw file contents", output_format="markdown", ) except docstrange.APIConnectionError as e: @@ -253,6 +196,7 @@ client = Docstrange( # Or, configure per-request: client.with_options(max_retries=5).extract.sync( + file=b"raw file contents", output_format="markdown", ) ``` @@ -278,6 +222,7 @@ client = Docstrange( # Override per-request: client.with_options(timeout=5.0).extract.sync( + file=b"raw file contents", output_format="markdown", ) ``` @@ -321,6 +266,7 @@ from docstrange import Docstrange client = Docstrange() response = client.extract.with_raw_response.sync( + file=b"raw file contents", output_format="markdown", ) print(response.headers.get('X-My-Header')) @@ -341,6 +287,7 @@ To stream the response body, use `.with_streaming_response` instead, which requi ```python with client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="markdown", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 7ccbce8..0cac7c9 100644 --- a/api.md +++ b/api.md @@ -3,67 +3,21 @@ Types: ```python -from docstrange.types import ( - BatchExtractRequestBody, - BatchExtractResponse, - ExtractRequestBody, - ExtractResponse, - ExtractionFormatResult, - ExtractionMetadata, - ExtractionResult, - StreamExtractRequestBody, - ExtractStreamResponse, -) +from docstrange.types import ExtractResponse ``` Methods: -- client.extract.async\_(\*\*params) -> ExtractResponse -- client.extract.batch(\*\*params) -> BatchExtractResponse -- client.extract.stream(\*\*params) -> str -- client.extract.sync(\*\*params) -> ExtractResponse - -## Results - -Types: - -```python -from docstrange.types.extract import ExtractionListResponse, PaginationInfo -``` - -Methods: - -- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] +- client.extract.sync(\*\*params) -> ExtractResponse # Classify Types: ```python -from docstrange.types import ( - BatchClassifyRequestBody, - BatchClassifyResponse, - ClassifyRequestBody, - ClassifyResponse, - FileClassificationResult, - PageClassification, -) +from docstrange.types import ClassifyResponse ``` Methods: -- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse - -# Chat - -Types: - -```python -from docstrange.types import ChatCompletionsRequest -``` - -Methods: - -- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 4c5bcc4..42d282d 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,10 +31,9 @@ ) if TYPE_CHECKING: - from .resources import chat, extract, classify - from .resources.chat import ChatResource, AsyncChatResource + from .resources import extract, classify + from .resources.extract import ExtractResource, AsyncExtractResource from .resources.classify import ClassifyResource, AsyncClassifyResource - from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -115,12 +114,6 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) - @cached_property - def chat(self) -> ChatResource: - from .resources.chat import ChatResource - - return ChatResource(self) - @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -301,12 +294,6 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) - @cached_property - def chat(self) -> AsyncChatResource: - from .resources.chat import AsyncChatResource - - return AsyncChatResource(self) - @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -438,12 +425,6 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithRawResponse: - from .resources.chat import ChatResourceWithRawResponse - - return ChatResourceWithRawResponse(self._client.chat) - class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -463,12 +444,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithRawResponse: - from .resources.chat import AsyncChatResourceWithRawResponse - - return AsyncChatResourceWithRawResponse(self._client.chat) - class DocstrangeWithStreamedResponse: _client: Docstrange @@ -488,12 +463,6 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithStreamingResponse: - from .resources.chat import ChatResourceWithStreamingResponse - - return ChatResourceWithStreamingResponse(self._client.chat) - class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -513,12 +482,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: - from .resources.chat import AsyncChatResourceWithStreamingResponse - - return AsyncChatResourceWithStreamingResponse(self._client.chat) - Client = Docstrange diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py deleted file mode 100644 index e926095..0000000 --- a/src/docstrange/pagination.py +++ /dev/null @@ -1,67 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Generic, TypeVar, Optional, cast -from typing_extensions import override - -from ._models import BaseModel -from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage - -__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] - -_T = TypeVar("_T") - - -class PageNumberPaginationPagination(BaseModel): - has_next: Optional[bool] = None - - total_count: Optional[int] = None - - -class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) - - -class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index a3172dd..6d7ff55 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) from .extract import ( ExtractResource, AsyncExtractResource, @@ -38,10 +30,4 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py deleted file mode 100644 index 0a892c1..0000000 --- a/src/docstrange/resources/chat.py +++ /dev/null @@ -1,218 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable - -import httpx - -from ..types import chat_create_completion_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """OpenAI-compatible chat completions endpoint. - - Supports text messages and - file/image inputs via `image_url` or `file_url` parts. - - **Limits and behavior:** - - - The backend enforces a model context limit of ~20,000 tokens (text + - multimodal tokens). - - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts - and embedded as data URLs. - - Inline data URLs are limited to ~64MB decoded size per item. - - For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. - - Args: - messages: Chat messages array - - model: Model identifier (e.g., nanonets/Nanonets-OCR-s) - - stream: Enable SSE streaming - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/chat/completions", - body=maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """OpenAI-compatible chat completions endpoint. - - Supports text messages and - file/image inputs via `image_url` or `file_url` parts. - - **Limits and behavior:** - - - The backend enforces a model context limit of ~20,000 tokens (text + - multimodal tokens). - - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts - and embedded as data URLs. - - Inline data URLs are limited to ~64MB decoded size per item. - - For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. - - Args: - messages: Chat messages array - - model: Model identifier (e.g., nanonets/Nanonets-OCR-s) - - stream: Enable SSE streaming - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/chat/completions", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_raw_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_raw_response_wrapper( - chat.create_completion, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_streamed_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_streamed_response_wrapper( - chat.create_completion, - ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index 5476a59..69045f4 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,11 +2,13 @@ from __future__ import annotations +from typing import Mapping, cast + import httpx -from ..types import classify_sync_params, classify_batch_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given -from .._utils import maybe_transform, async_maybe_transform +from ..types import classify_sync_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,7 +19,6 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse -from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -42,56 +43,6 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) - def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50 files). - - Files are queued for background processing. Use - `GET /api/v1/extract/results/{record_id}` to poll results. - - Args: - categories: JSON array of category objects (max 50 categories) - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/classify/batch", - body=maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - def sync( self, *, @@ -105,17 +56,12 @@ def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document into predefined categories synchronously. - - Each page is classified individually with category, confidence score (0-100), - and reasoning. + Classify a single document. Args: - categories: - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] + categories: JSON array of categories - file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) + file: File to classify extra_headers: Send extra headers @@ -125,19 +71,21 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -165,56 +113,6 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) - async def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50 files). - - Files are queued for background processing. Use - `GET /api/v1/extract/results/{record_id}` to poll results. - - Args: - categories: JSON array of category objects (max 50 categories) - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/classify/batch", - body=await async_maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - async def sync( self, *, @@ -228,17 +126,12 @@ async def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document into predefined categories synchronously. - - Each page is classified individually with category, confidence score (0-100), - and reasoning. + Classify a single document. Args: - categories: - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] + categories: JSON array of categories - file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) + file: File to classify extra_headers: Send extra headers @@ -248,19 +141,21 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -272,9 +167,6 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_raw_response_wrapper( - classify.batch, - ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -284,9 +176,6 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_raw_response_wrapper( - classify.batch, - ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -296,9 +185,6 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_streamed_response_wrapper( - classify.batch, - ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -308,9 +194,6 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_streamed_response_wrapper( - classify.batch, - ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract.py b/src/docstrange/resources/extract.py new file mode 100644 index 0000000..88bef17 --- /dev/null +++ b/src/docstrange/resources/extract.py @@ -0,0 +1,199 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx + +from ..types import extract_sync_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.extract_response import ExtractResponse + +__all__ = ["ExtractResource", "AsyncExtractResource"] + + +class ExtractResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ExtractResourceWithStreamingResponse(self) + + def sync( + self, + *, + file: FileTypes, + output_format: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload + + output_format: Output format + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class AsyncExtractResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncExtractResourceWithStreamingResponse(self) + + async def sync( + self, + *, + file: FileTypes, + output_format: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload + + output_format: Output format + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class ExtractResourceWithRawResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.sync = to_raw_response_wrapper( + extract.sync, + ) + + +class AsyncExtractResourceWithRawResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.sync = async_to_raw_response_wrapper( + extract.sync, + ) + + +class ExtractResourceWithStreamingResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.sync = to_streamed_response_wrapper( + extract.sync, + ) + + +class AsyncExtractResourceWithStreamingResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.sync = async_to_streamed_response_wrapper( + extract.sync, + ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py deleted file mode 100644 index 22cd257..0000000 --- a/src/docstrange/resources/extract/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .extract import ( - ExtractResource, - AsyncExtractResource, - ExtractResourceWithRawResponse, - AsyncExtractResourceWithRawResponse, - ExtractResourceWithStreamingResponse, - AsyncExtractResourceWithStreamingResponse, -) -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) - -__all__ = [ - "ResultsResource", - "AsyncResultsResource", - "ResultsResourceWithRawResponse", - "AsyncResultsResourceWithRawResponse", - "ResultsResourceWithStreamingResponse", - "AsyncResultsResourceWithStreamingResponse", - "ExtractResource", - "AsyncExtractResource", - "ExtractResourceWithRawResponse", - "AsyncExtractResourceWithRawResponse", - "ExtractResourceWithStreamingResponse", - "AsyncExtractResourceWithStreamingResponse", -] diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py deleted file mode 100644 index bed3b08..0000000 --- a/src/docstrange/resources/extract/extract.py +++ /dev/null @@ -1,814 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) -from ..._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._streaming import Stream, AsyncStream -from ..._base_client import make_request_options -from ...types.extract_response import ExtractResponse -from ...types.batch_extract_response import BatchExtractResponse -from ...types.extract_stream_response import ExtractStreamResponse - -__all__ = ["ExtractResource", "AsyncExtractResource"] - - -class ExtractResource(SyncAPIResource): - @cached_property - def results(self) -> ResultsResource: - return ResultsResource(self._client) - - @cached_property - def with_raw_response(self) -> ExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ExtractResourceWithStreamingResponse(self) - - def async_( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Queue a document for background processing. - - Returns a `record_id` to poll - results via `GET /api/v1/extract/results/{record_id}`. - - Recommended for large documents (>50 pages). - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/async", - body=maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """Process multiple files asynchronously (max 50 files). - - All files share the same - extraction options. - - Args: - files: Files to process (max 50) - - output_format: Output format(s) for all files - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/batch", - body=maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - def stream( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[ExtractStreamResponse]: - """ - Stream extraction results via Server-Sent Events (SSE) for real-time content - delivery. - - **Event Types:** - - - `content`: Incremental content chunks (streaming mode) - - `complete`: Full content at once (batch mode) - - `done`: Final event with record_id and processing_time - - `error`: Error information - - `async_queued`: Large files queued for async processing - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for - multiple. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch - mode. - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/stream", - body=maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=Stream[ExtractStreamResponse], - ) - - def sync( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Process a document and return extracted content immediately. - - (Works for files - with 5 pages or less.) - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/sync", - body=maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class AsyncExtractResource(AsyncAPIResource): - @cached_property - def results(self) -> AsyncResultsResource: - return AsyncResultsResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncExtractResourceWithStreamingResponse(self) - - async def async_( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Queue a document for background processing. - - Returns a `record_id` to poll - results via `GET /api/v1/extract/results/{record_id}`. - - Recommended for large documents (>50 pages). - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/async", - body=await async_maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - async def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """Process multiple files asynchronously (max 50 files). - - All files share the same - extraction options. - - Args: - files: Files to process (max 50) - - output_format: Output format(s) for all files - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/batch", - body=await async_maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - async def stream( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[ExtractStreamResponse]: - """ - Stream extraction results via Server-Sent Events (SSE) for real-time content - delivery. - - **Event Types:** - - - `content`: Incremental content chunks (streaming mode) - - `complete`: Full content at once (batch mode) - - `done`: Final event with record_id and processing_time - - `error`: Error information - - `async_queued`: Large files queued for async processing - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for - multiple. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch - mode. - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/stream", - body=await async_maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=AsyncStream[ExtractStreamResponse], - ) - - async def sync( - self, - *, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file: FileTypes | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Process a document and return extracted content immediately. - - (Works for files - with 5 pages or less.) - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform( - { - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file": file, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class ExtractResourceWithRawResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.async_ = to_raw_response_wrapper( - extract.async_, - ) - self.batch = to_raw_response_wrapper( - extract.batch, - ) - self.stream = to_raw_response_wrapper( - extract.stream, - ) - self.sync = to_raw_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> ResultsResourceWithRawResponse: - return ResultsResourceWithRawResponse(self._extract.results) - - -class AsyncExtractResourceWithRawResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.async_ = async_to_raw_response_wrapper( - extract.async_, - ) - self.batch = async_to_raw_response_wrapper( - extract.batch, - ) - self.stream = async_to_raw_response_wrapper( - extract.stream, - ) - self.sync = async_to_raw_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> AsyncResultsResourceWithRawResponse: - return AsyncResultsResourceWithRawResponse(self._extract.results) - - -class ExtractResourceWithStreamingResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.async_ = to_streamed_response_wrapper( - extract.async_, - ) - self.batch = to_streamed_response_wrapper( - extract.batch, - ) - self.stream = to_streamed_response_wrapper( - extract.stream, - ) - self.sync = to_streamed_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> ResultsResourceWithStreamingResponse: - return ResultsResourceWithStreamingResponse(self._extract.results) - - -class AsyncExtractResourceWithStreamingResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.async_ = async_to_streamed_response_wrapper( - extract.async_, - ) - self.batch = async_to_streamed_response_wrapper( - extract.batch, - ) - self.stream = async_to_streamed_response_wrapper( - extract.stream, - ) - self.sync = async_to_streamed_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> AsyncResultsResourceWithStreamingResponse: - return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py deleted file mode 100644 index 1533ac2..0000000 --- a/src/docstrange/resources/extract/results.py +++ /dev/null @@ -1,294 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination -from ..._base_client import AsyncPaginator, make_request_options -from ...types.extract import result_list_params, result_retrieve_params -from ...types.extract_response import ExtractResponse - -__all__ = ["ResultsResource", "AsyncResultsResource"] - - -class ResultsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ResultsResourceWithStreamingResponse(self) - - def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - include_content: Include full extracted content (default: true) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageNumberPagination[ExtractResponse]: - """ - List all extraction jobs for the authenticated user (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=SyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class AsyncResultsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncResultsResourceWithStreamingResponse(self) - - async def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - include_content: Include full extracted content (default: true) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return await self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: - """ - List all extraction jobs for the authenticated user (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=AsyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class ResultsResourceWithRawResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_raw_response_wrapper( - results.retrieve, - ) - self.list = to_raw_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithRawResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_raw_response_wrapper( - results.retrieve, - ) - self.list = async_to_raw_response_wrapper( - results.list, - ) - - -class ResultsResourceWithStreamingResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_streamed_response_wrapper( - results.retrieve, - ) - self.list = to_streamed_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithStreamingResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_streamed_response_wrapper( - results.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - results.list, - ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index a18a10a..fac3955 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,18 +4,5 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse -from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams -from .extraction_metadata import ExtractionMetadata as ExtractionMetadata -from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams -from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams -from .extract_batch_params import ExtractBatchParams as ExtractBatchParams -from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams -from .extract_stream_params import ExtractStreamParams as ExtractStreamParams -from .batch_extract_response import BatchExtractResponse as BatchExtractResponse -from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse -from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse -from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult -from .file_classification_result import FileClassificationResult as FileClassificationResult -from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py deleted file mode 100644 index a36e5a8..0000000 --- a/src/docstrange/types/batch_classify_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .file_classification_result import FileClassificationResult - -__all__ = ["BatchClassifyResponse"] - - -class BatchClassifyResponse(BaseModel): - batch_id: str - - failed_files: int - - message: str - - results: List[FileClassificationResult] - - success: bool - - successful_files: int - - total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py deleted file mode 100644 index 4e73773..0000000 --- a/src/docstrange/types/batch_extract_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .extract_response import ExtractResponse - -__all__ = ["BatchExtractResponse"] - - -class BatchExtractResponse(BaseModel): - accepted_files: int - - batch_id: str - - message: str - - records: List[ExtractResponse] - - rejected_files: int - - success: bool - - total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py deleted file mode 100644 index b7cde5c..0000000 --- a/src/docstrange/types/chat_create_completion_params.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, TypedDict - -__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] - - -class ChatCreateCompletionParams(TypedDict, total=False): - messages: Required[Iterable[Message]] - """Chat messages array""" - - model: Required[str] - """Model identifier (e.g., nanonets/Nanonets-OCR-s)""" - - stream: bool - """Enable SSE streaming""" - - -class MessageContentFileURL(TypedDict, total=False): - url: str - - -class MessageContentImageURL(TypedDict, total=False): - url: str - - -class MessageContent(TypedDict, total=False): - file_url: MessageContentFileURL - - image_url: MessageContentImageURL - - text: str - - type: str - - -class Message(TypedDict, total=False): - content: Required[Iterable[MessageContent]] - """Message content as an array of parts (text or file/image URLs).""" - - role: Required[str] - """Message role (system, user, assistant)""" diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py deleted file mode 100644 index 5cac9e3..0000000 --- a/src/docstrange/types/classify_batch_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ClassifyBatchParams"] - - -class ClassifyBatchParams(TypedDict, total=False): - categories: Required[str] - """JSON array of category objects (max 50 categories)""" - - files: Required[SequenceNotStr[FileTypes]] - """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index fdebbb0..5bab3de 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,9 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional - from .._models import BaseModel -from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -12,5 +9,3 @@ class ClassifyResponse(BaseModel): message: str success: bool - - result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py index 772427d..ff11627 100644 --- a/src/docstrange/types/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -11,10 +11,7 @@ class ClassifySyncParams(TypedDict, total=False): categories: Required[str] - """ - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] - """ + """JSON array of categories""" file: Required[FileTypes] - """File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP)""" + """File to classify""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py deleted file mode 100644 index 387a007..0000000 --- a/src/docstrange/types/extract/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .pagination_info import PaginationInfo as PaginationInfo -from .result_list_params import ResultListParams as ResultListParams -from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams -from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py deleted file mode 100644 index 26a12e7..0000000 --- a/src/docstrange/types/extract/extraction_list_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel -from .pagination_info import PaginationInfo -from ..extract_response import ExtractResponse - -__all__ = ["ExtractionListResponse"] - - -class ExtractionListResponse(BaseModel): - pagination: PaginationInfo - - results: List[ExtractResponse] - - success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py deleted file mode 100644 index 7a678a9..0000000 --- a/src/docstrange/types/extract/pagination_info.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["PaginationInfo"] - - -class PaginationInfo(BaseModel): - has_next: Optional[bool] = None - - has_previous: Optional[bool] = None - - page: Optional[int] = None - - page_size: Optional[int] = None - - total_count: Optional[int] = None - - total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py deleted file mode 100644 index 7dfe38a..0000000 --- a/src/docstrange/types/extract/result_list_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, TypedDict - -__all__ = ["ResultListParams"] - - -class ResultListParams(TypedDict, total=False): - page: int - - page_size: int - - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - - sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py deleted file mode 100644 index 539b80d..0000000 --- a/src/docstrange/types/extract/result_retrieve_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["ResultRetrieveParams"] - - -class ResultRetrieveParams(TypedDict, total=False): - include_content: bool - """Include full extracted content (default: true)""" diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py deleted file mode 100644 index d638b09..0000000 --- a/src/docstrange/types/extract_async_params.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractAsyncParams"] - - -class ExtractAsyncParams(TypedDict, total=False): - output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple (e.g., `markdown,json`). - """ - - csv_options: str - """CSV extraction options (e.g., `table`)""" - - custom_instructions: str - """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - - file: FileTypes - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options. - - Values: `hierarchy_output`, `table-of-contents`, field list - `["field1", "field2"]`, or JSON schema `{...}` - """ - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py deleted file mode 100644 index bdc7b29..0000000 --- a/src/docstrange/types/extract_batch_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ExtractBatchParams"] - - -class ExtractBatchParams(TypedDict, total=False): - files: Required[SequenceNotStr[FileTypes]] - """Files to process (max 50)""" - - output_format: Required[str] - """Output format(s) for all files""" - - csv_options: str - - custom_instructions: str - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index 9d93ead..ba021e0 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,38 +1,17 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional -from datetime import datetime -from typing_extensions import Literal from .._models import BaseModel -from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): - message: str - record_id: str - """Job ID for retrieving results""" - status: Literal["completed", "processing", "failed"] + status: str success: bool - created_at: Optional[datetime] = None - - file_size: Optional[int] = None - """Size in bytes""" - - filename: Optional[str] = None - - output_format: Optional[str] = None - - pages_processed: Optional[int] = None - - processing_time: Optional[float] = None - """Time in seconds""" - - result: Optional[ExtractionResult] = None - """Results by format (only requested formats populated)""" + message: Optional[str] = None diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py deleted file mode 100644 index 2a8e9e3..0000000 --- a/src/docstrange/types/extract_stream_params.py +++ /dev/null @@ -1,47 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractStreamParams"] - - -class ExtractStreamParams(TypedDict, total=False): - output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple. - """ - - csv_options: str - """CSV extraction options""" - - custom_instructions: str - """Custom extraction instructions""" - - enable_streaming: bool - """Enable real-time streaming. - - If false, returns complete content via SSE batch mode. - """ - - file: FileTypes - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options""" - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py deleted file mode 100644 index 333ff97..0000000 --- a/src/docstrange/types/extract_stream_response.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import TypeAlias - -__all__ = ["ExtractStreamResponse"] - -ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index e3c4bea..242ce95 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict from .._types import FileTypes @@ -10,36 +10,8 @@ class ExtractSyncParams(TypedDict, total=False): - output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple (e.g., `markdown,json`). - """ - - csv_options: str - """CSV extraction options (e.g., `table`)""" - - custom_instructions: str - """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - - file: FileTypes - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - file_base64: str - """Base64-encoded file content""" + file: Required[FileTypes] + """File to upload""" - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options. - - Values: `hierarchy_output`, `table-of-contents`, field list - `["field1", "field2"]`, or JSON schema `{...}` - """ - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" + output_format: Required[str] + """Output format""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py deleted file mode 100644 index 579500c..0000000 --- a/src/docstrange/types/extraction_format_result.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional - -from .._models import BaseModel -from .extraction_metadata import ExtractionMetadata - -__all__ = ["ExtractionFormatResult"] - - -class ExtractionFormatResult(BaseModel): - content: Union[str, object] - """Extracted content""" - - metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py deleted file mode 100644 index 9792ba0..0000000 --- a/src/docstrange/types/extraction_metadata.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["ExtractionMetadata"] - - -class ExtractionMetadata(BaseModel): - bounding_boxes: Optional[object] = None - """Element coordinates with page dimensions""" - - confidence_score: Optional[object] = None - """Confidence scores per field""" diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py deleted file mode 100644 index 57b2928..0000000 --- a/src/docstrange/types/extraction_result.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from pydantic import Field as FieldInfo - -from .._models import BaseModel -from .extraction_format_result import ExtractionFormatResult - -__all__ = ["ExtractionResult"] - - -class ExtractionResult(BaseModel): - """Results by format (only requested formats populated)""" - - csv: Optional[ExtractionFormatResult] = None - - html: Optional[ExtractionFormatResult] = None - - json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) - - markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py deleted file mode 100644 index 397d924..0000000 --- a/src/docstrange/types/file_classification_result.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional - -from .._models import BaseModel -from .page_classification import PageClassification - -__all__ = ["FileClassificationResult"] - - -class FileClassificationResult(BaseModel): - filename: str - - pages: List[PageClassification] - - total_pages: int - - error: Optional[str] = None - - processing_time: Optional[float] = None - """Time in seconds""" - - record_id: Optional[str] = None - """Record ID for polling results""" diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py deleted file mode 100644 index 2099524..0000000 --- a/src/docstrange/types/page_classification.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["PageClassification"] - - -class PageClassification(BaseModel): - category: str - """Matched category name or 'Other' if no match""" - - confidence: int - """Confidence score (0-100)""" - - page_number: int - - reasoning: str - """Explanation for the classification""" - - identified_category: Optional[str] = None - """ - Specific document type identified by the model (e.g., - 'Invoice_Amazon_Electronics'). Always provided - contains a more detailed - classification than the category. - """ diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/extract/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py deleted file mode 100644 index ee1d5f5..0000000 --- a/tests/api_resources/extract/test_results.py +++ /dev/null @@ -1,205 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse -from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestResults: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_retrieve(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_retrieve(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_retrieve(self, client: Docstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list(self, client: Docstrange) -> None: - result = client.extract.results.list() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_list(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_list(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncResults: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - await async_client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py deleted file mode 100644 index cbcd7c3..0000000 --- a/tests/api_resources/test_chat.py +++ /dev/null @@ -1,183 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestChat: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "https://example.com/doc.pdf"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "file_url", - }, - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "Extract the text from the document.", - "type": "text", - }, - ], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - stream=False, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_create_completion(self, client: Docstrange) -> None: - response = client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_create_completion(self, client: Docstrange) -> None: - with client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncChat: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "https://example.com/doc.pdf"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "file_url", - }, - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "Extract the text from the document.", - "type": "text", - }, - ], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - stream=False, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: - response = await async_client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: - async with async_client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}, {}], - "role": "user", - } - ], - model="nanonets/Nanonets-OCR-s", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index b4377f6..63539df 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse, BatchClassifyResponse +from docstrange.types import ClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,48 +17,11 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - classify = client.classify.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.classify.with_raw_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.classify.with_streaming_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: classify = client.classify.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -67,7 +30,7 @@ def test_method_sync(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.classify.with_raw_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) @@ -80,7 +43,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.classify.with_streaming_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) as response: assert not response.is_closed @@ -97,48 +60,11 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.classify.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.classify.with_raw_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.classify.with_streaming_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: classify = await async_client.classify.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -147,7 +73,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.classify.with_raw_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) @@ -160,7 +86,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.classify.with_streaming_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index ffdac33..d8117c2 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,10 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ( - ExtractResponse, - BatchExtractResponse, -) +from docstrange.types import ExtractResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,178 +17,12 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async(self, client: Docstrange) -> None: - extract = client.extract.async_( - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.async_( - output_format="markdown", - csv_options="", - custom_instructions="", - file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_async(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.async_( - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_async(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.async_( - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - output_format="markdown", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream_with_all_params(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_raw_response_stream(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.stream( - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = response.parse() - stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_streaming_response_stream(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.stream( - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = response.parse() - stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: extract = client.extract.sync( - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_sync_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.sync( - output_format="markdown", - csv_options="", - custom_instructions="", file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -199,7 +30,8 @@ def test_method_sync_with_all_params(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.extract.with_raw_response.sync( - output_format="markdown", + file=b"raw file contents", + output_format="output_format", ) assert response.is_closed is True @@ -211,7 +43,8 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.extract.with_streaming_response.sync( - output_format="markdown", + file=b"raw file contents", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -227,178 +60,12 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - output_format="markdown", - csv_options="", - custom_instructions="", - file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.async_( - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.async_( - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - output_format="markdown", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.stream( - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = await response.parse() - await stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.stream( - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = await response.parse() - await stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.sync( - output_format="markdown", - csv_options="", - custom_instructions="", file=b"raw file contents", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -406,7 +73,8 @@ async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.sync( - output_format="markdown", + file=b"raw file contents", + output_format="output_format", ) assert response.is_closed is True @@ -418,7 +86,8 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.sync( - output_format="markdown", + file=b"raw file contents", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index 3d576f6..2756693 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,7 +862,9 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.extract.with_streaming_response.sync(output_format="markdown").__enter__() + client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__enter__() assert _get_open_connections(client) == 0 @@ -872,7 +874,9 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.extract.with_streaming_response.sync(output_format="markdown").__enter__() + client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -901,7 +905,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.extract.with_raw_response.sync(output_format="markdown") + response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -926,7 +930,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -951,7 +955,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1768,7 +1772,9 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() + await async_client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1780,7 +1786,9 @@ async def test_retrying_status_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() + await async_client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1809,7 +1817,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.extract.with_raw_response.sync(output_format="markdown") + response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1834,7 +1842,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1859,7 +1867,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From a1ada95fffb116ab7c5a59f5d69eb24fa69f5827 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:29:19 +0000 Subject: [PATCH 14/26] feat(api): api update --- .stats.yml | 8 +- README.md | 63 +- api.md | 52 +- src/docstrange/_client.py | 41 +- src/docstrange/pagination.py | 67 ++ src/docstrange/resources/__init__.py | 14 + src/docstrange/resources/chat.py | 218 +++++ src/docstrange/resources/classify.py | 175 +++- src/docstrange/resources/extract.py | 199 ----- src/docstrange/resources/extract/__init__.py | 33 + src/docstrange/resources/extract/extract.py | 814 ++++++++++++++++++ src/docstrange/resources/extract/results.py | 294 +++++++ src/docstrange/types/__init__.py | 13 + .../types/batch_classify_response.py | 24 + .../types/batch_extract_response.py | 24 + .../types/chat_create_completion_params.py | 45 + src/docstrange/types/classify_batch_params.py | 17 + src/docstrange/types/classify_response.py | 5 + src/docstrange/types/classify_sync_params.py | 7 +- src/docstrange/types/extract/__init__.py | 8 + .../types/extract/extraction_list_response.py | 17 + .../types/extract/pagination_info.py | 21 + .../types/extract/result_list_params.py | 17 + .../types/extract/result_retrieve_params.py | 12 + src/docstrange/types/extract_async_params.py | 45 + src/docstrange/types/extract_batch_params.py | 27 + src/docstrange/types/extract_response.py | 25 +- src/docstrange/types/extract_stream_params.py | 47 + .../types/extract_stream_response.py | 7 + src/docstrange/types/extract_sync_params.py | 34 +- .../types/extraction_format_result.py | 15 + src/docstrange/types/extraction_metadata.py | 15 + src/docstrange/types/extraction_result.py | 22 + .../types/file_classification_result.py | 24 + src/docstrange/types/page_classification.py | 27 + tests/api_resources/extract/__init__.py | 1 + tests/api_resources/extract/test_results.py | 205 +++++ tests/api_resources/test_chat.py | 183 ++++ tests/api_resources/test_classify.py | 88 +- tests/api_resources/test_extract.py | 363 +++++++- tests/test_client.py | 24 +- 41 files changed, 3067 insertions(+), 273 deletions(-) create mode 100644 src/docstrange/pagination.py create mode 100644 src/docstrange/resources/chat.py delete mode 100644 src/docstrange/resources/extract.py create mode 100644 src/docstrange/resources/extract/__init__.py create mode 100644 src/docstrange/resources/extract/extract.py create mode 100644 src/docstrange/resources/extract/results.py create mode 100644 src/docstrange/types/batch_classify_response.py create mode 100644 src/docstrange/types/batch_extract_response.py create mode 100644 src/docstrange/types/chat_create_completion_params.py create mode 100644 src/docstrange/types/classify_batch_params.py create mode 100644 src/docstrange/types/extract/__init__.py create mode 100644 src/docstrange/types/extract/extraction_list_response.py create mode 100644 src/docstrange/types/extract/pagination_info.py create mode 100644 src/docstrange/types/extract/result_list_params.py create mode 100644 src/docstrange/types/extract/result_retrieve_params.py create mode 100644 src/docstrange/types/extract_async_params.py create mode 100644 src/docstrange/types/extract_batch_params.py create mode 100644 src/docstrange/types/extract_stream_params.py create mode 100644 src/docstrange/types/extract_stream_response.py create mode 100644 src/docstrange/types/extraction_format_result.py create mode 100644 src/docstrange/types/extraction_metadata.py create mode 100644 src/docstrange/types/extraction_result.py create mode 100644 src/docstrange/types/file_classification_result.py create mode 100644 src/docstrange/types/page_classification.py create mode 100644 tests/api_resources/extract/__init__.py create mode 100644 tests/api_resources/extract/test_results.py create mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 06a3238..a5ffc81 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-00ea96039f73fd253b5042c3e5c1ae800ca6b3bbde585408d20a46f73ae87ecf.yml -openapi_spec_hash: b4046d95714426b15edb8ac003f904c6 -config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 +configured_endpoints: 9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-1f31f87d4c6e168ffcccab94b8967a095195cac1ea2518bbddbeaf4611e02f05.yml +openapi_spec_hash: 18bec87d90926b0bc0cd98ce1d066d1f +config_hash: c02183ee58f815311de9278705163d1a diff --git a/README.md b/README.md index 622faec..991dcba 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,67 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Docstrange API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from docstrange import Docstrange + +client = Docstrange() + +all_results = [] +# Automatically fetches more pages as needed. +for result in client.extract.results.list(): + # Do something with result here + all_results.append(result) +print(all_results) +``` + +Or, asynchronously: + +```python +import asyncio +from docstrange import AsyncDocstrange + +client = AsyncDocstrange() + + +async def main() -> None: + all_results = [] + # Iterate through items across all pages, issuing requests as needed. + async for result in client.extract.results.list(): + all_results.append(result) + print(all_results) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.extract.results.list() +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.results)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.extract.results.list() +for result in first_page.results: + print(result.record_id) + +# Remove `await` for non-async usage. +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. @@ -127,7 +188,7 @@ client = Docstrange() client.extract.sync( file=Path("/path/to/file"), - output_format="output_format", + output_format="markdown", ) ``` diff --git a/api.md b/api.md index 0cac7c9..7ccbce8 100644 --- a/api.md +++ b/api.md @@ -3,21 +3,67 @@ Types: ```python -from docstrange.types import ExtractResponse +from docstrange.types import ( + BatchExtractRequestBody, + BatchExtractResponse, + ExtractRequestBody, + ExtractResponse, + ExtractionFormatResult, + ExtractionMetadata, + ExtractionResult, + StreamExtractRequestBody, + ExtractStreamResponse, +) ``` Methods: -- client.extract.sync(\*\*params) -> ExtractResponse +- client.extract.async\_(\*\*params) -> ExtractResponse +- client.extract.batch(\*\*params) -> BatchExtractResponse +- client.extract.stream(\*\*params) -> str +- client.extract.sync(\*\*params) -> ExtractResponse + +## Results + +Types: + +```python +from docstrange.types.extract import ExtractionListResponse, PaginationInfo +``` + +Methods: + +- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse +- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] # Classify Types: ```python -from docstrange.types import ClassifyResponse +from docstrange.types import ( + BatchClassifyRequestBody, + BatchClassifyResponse, + ClassifyRequestBody, + ClassifyResponse, + FileClassificationResult, + PageClassification, +) ``` Methods: +- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse + +# Chat + +Types: + +```python +from docstrange.types import ChatCompletionsRequest +``` + +Methods: + +- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 42d282d..4c5bcc4 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,9 +31,10 @@ ) if TYPE_CHECKING: - from .resources import extract, classify - from .resources.extract import ExtractResource, AsyncExtractResource + from .resources import chat, extract, classify + from .resources.chat import ChatResource, AsyncChatResource from .resources.classify import ClassifyResource, AsyncClassifyResource + from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -114,6 +115,12 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) + @cached_property + def chat(self) -> ChatResource: + from .resources.chat import ChatResource + + return ChatResource(self) + @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -294,6 +301,12 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) + @cached_property + def chat(self) -> AsyncChatResource: + from .resources.chat import AsyncChatResource + + return AsyncChatResource(self) + @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -425,6 +438,12 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithRawResponse: + from .resources.chat import ChatResourceWithRawResponse + + return ChatResourceWithRawResponse(self._client.chat) + class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -444,6 +463,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithRawResponse: + from .resources.chat import AsyncChatResourceWithRawResponse + + return AsyncChatResourceWithRawResponse(self._client.chat) + class DocstrangeWithStreamedResponse: _client: Docstrange @@ -463,6 +488,12 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithStreamingResponse: + from .resources.chat import ChatResourceWithStreamingResponse + + return ChatResourceWithStreamingResponse(self._client.chat) + class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -482,6 +513,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: + from .resources.chat import AsyncChatResourceWithStreamingResponse + + return AsyncChatResourceWithStreamingResponse(self._client.chat) + Client = Docstrange diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py new file mode 100644 index 0000000..e926095 --- /dev/null +++ b/src/docstrange/pagination.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional, cast +from typing_extensions import override + +from ._models import BaseModel +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] + +_T = TypeVar("_T") + + +class PageNumberPaginationPagination(BaseModel): + has_next: Optional[bool] = None + + total_count: Optional[int] = None + + +class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) + + +class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index 6d7ff55..a3172dd 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) from .extract import ( ExtractResource, AsyncExtractResource, @@ -30,4 +38,10 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py new file mode 100644 index 0000000..0a892c1 --- /dev/null +++ b/src/docstrange/resources/chat.py @@ -0,0 +1,218 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..types import chat_create_completion_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """OpenAI-compatible chat completions endpoint. + + Supports text messages and + file/image inputs via `image_url` or `file_url` parts. + + **Limits and behavior:** + + - The backend enforces a model context limit of ~20,000 tokens (text + + multimodal tokens). + - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts + and embedded as data URLs. + - Inline data URLs are limited to ~64MB decoded size per item. + + For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + + Args: + messages: Chat messages array + + model: Model identifier (e.g., nanonets/Nanonets-OCR-s) + + stream: Enable SSE streaming + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """OpenAI-compatible chat completions endpoint. + + Supports text messages and + file/image inputs via `image_url` or `file_url` parts. + + **Limits and behavior:** + + - The backend enforces a model context limit of ~20,000 tokens (text + + multimodal tokens). + - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts + and embedded as data URLs. + - Inline data URLs are limited to ~64MB decoded size per item. + + For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + + Args: + messages: Chat messages array + + model: Model identifier (e.g., nanonets/Nanonets-OCR-s) + + stream: Enable SSE streaming + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_raw_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_raw_response_wrapper( + chat.create_completion, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_streamed_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_streamed_response_wrapper( + chat.create_completion, + ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index 69045f4..5476a59 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,13 +2,11 @@ from __future__ import annotations -from typing import Mapping, cast - import httpx -from ..types import classify_sync_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..types import classify_sync_params, classify_batch_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -19,6 +17,7 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse +from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -43,6 +42,56 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) + def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50 files). + + Files are queued for background processing. Use + `GET /api/v1/extract/results/{record_id}` to poll results. + + Args: + categories: JSON array of category objects (max 50 categories) + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/classify/batch", + body=maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + def sync( self, *, @@ -56,12 +105,17 @@ def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document. + Classify a single document into predefined categories synchronously. + + Each page is classified individually with category, confidence score (0-100), + and reasoning. Args: - categories: JSON array of categories + categories: + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] - file: File to classify + file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) extra_headers: Send extra headers @@ -71,21 +125,19 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -113,6 +165,56 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) + async def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50 files). + + Files are queued for background processing. Use + `GET /api/v1/extract/results/{record_id}` to poll results. + + Args: + categories: JSON array of category objects (max 50 categories) + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/classify/batch", + body=await async_maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + async def sync( self, *, @@ -126,12 +228,17 @@ async def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document. + Classify a single document into predefined categories synchronously. + + Each page is classified individually with category, confidence score (0-100), + and reasoning. Args: - categories: JSON array of categories + categories: + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] - file: File to classify + file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) extra_headers: Send extra headers @@ -141,21 +248,19 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=await async_maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -167,6 +272,9 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_raw_response_wrapper( + classify.batch, + ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -176,6 +284,9 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_raw_response_wrapper( + classify.batch, + ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -185,6 +296,9 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_streamed_response_wrapper( + classify.batch, + ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -194,6 +308,9 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_streamed_response_wrapper( + classify.batch, + ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract.py b/src/docstrange/resources/extract.py deleted file mode 100644 index 88bef17..0000000 --- a/src/docstrange/resources/extract.py +++ /dev/null @@ -1,199 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Mapping, cast - -import httpx - -from ..types import extract_sync_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.extract_response import ExtractResponse - -__all__ = ["ExtractResource", "AsyncExtractResource"] - - -class ExtractResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ExtractResourceWithStreamingResponse(self) - - def sync( - self, - *, - file: FileTypes, - output_format: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload - - output_format: Output format - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/sync", - body=maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class AsyncExtractResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncExtractResourceWithStreamingResponse(self) - - async def sync( - self, - *, - file: FileTypes, - output_format: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload - - output_format: Output format - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class ExtractResourceWithRawResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_raw_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithRawResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_raw_response_wrapper( - extract.sync, - ) - - -class ExtractResourceWithStreamingResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_streamed_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithStreamingResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_streamed_response_wrapper( - extract.sync, - ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py new file mode 100644 index 0000000..22cd257 --- /dev/null +++ b/src/docstrange/resources/extract/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .extract import ( + ExtractResource, + AsyncExtractResource, + ExtractResourceWithRawResponse, + AsyncExtractResourceWithRawResponse, + ExtractResourceWithStreamingResponse, + AsyncExtractResourceWithStreamingResponse, +) +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) + +__all__ = [ + "ResultsResource", + "AsyncResultsResource", + "ResultsResourceWithRawResponse", + "AsyncResultsResourceWithRawResponse", + "ResultsResourceWithStreamingResponse", + "AsyncResultsResourceWithStreamingResponse", + "ExtractResource", + "AsyncExtractResource", + "ExtractResourceWithRawResponse", + "AsyncExtractResourceWithRawResponse", + "ExtractResourceWithStreamingResponse", + "AsyncExtractResourceWithStreamingResponse", +] diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py new file mode 100644 index 0000000..1cca021 --- /dev/null +++ b/src/docstrange/resources/extract/extract.py @@ -0,0 +1,814 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.extract_response import ExtractResponse +from ...types.batch_extract_response import BatchExtractResponse +from ...types.extract_stream_response import ExtractStreamResponse + +__all__ = ["ExtractResource", "AsyncExtractResource"] + + +class ExtractResource(SyncAPIResource): + @cached_property + def results(self) -> ResultsResource: + return ResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> ExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ExtractResourceWithStreamingResponse(self) + + def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Queue a document for background processing. + + Returns a `record_id` to poll + results via `GET /api/v1/extract/results/{record_id}`. + + Recommended for large documents (>50 pages). + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/async", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """Process multiple files asynchronously (max 50 files). + + All files share the same + extraction options. + + Args: + files: Files to process (max 50) + + output_format: Output format(s) for all files + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/batch", + body=maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ExtractStreamResponse]: + """ + Stream extraction results via Server-Sent Events (SSE) for real-time content + delivery. + + **Event Types:** + + - `content`: Incremental content chunks (streaming mode) + - `complete`: Full content at once (batch mode) + - `done`: Final event with record_id and processing_time + - `error`: Error information + - `async_queued`: Large files queued for async processing + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for + multiple. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch + mode. + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/stream", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=Stream[ExtractStreamResponse], + ) + + def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Process a document and return extracted content immediately. + + (Works for files + with 5 pages or less.) + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class AsyncExtractResource(AsyncAPIResource): + @cached_property + def results(self) -> AsyncResultsResource: + return AsyncResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncExtractResourceWithStreamingResponse(self) + + async def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Queue a document for background processing. + + Returns a `record_id` to poll + results via `GET /api/v1/extract/results/{record_id}`. + + Recommended for large documents (>50 pages). + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/async", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + async def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """Process multiple files asynchronously (max 50 files). + + All files share the same + extraction options. + + Args: + files: Files to process (max 50) + + output_format: Output format(s) for all files + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/batch", + body=await async_maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + async def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ExtractStreamResponse]: + """ + Stream extraction results via Server-Sent Events (SSE) for real-time content + delivery. + + **Event Types:** + + - `content`: Incremental content chunks (streaming mode) + - `complete`: Full content at once (batch mode) + - `done`: Final event with record_id and processing_time + - `error`: Error information + - `async_queued`: Large files queued for async processing + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for + multiple. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch + mode. + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/stream", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=AsyncStream[ExtractStreamResponse], + ) + + async def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Process a document and return extracted content immediately. + + (Works for files + with 5 pages or less.) + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class ExtractResourceWithRawResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_raw_response_wrapper( + extract.async_, + ) + self.batch = to_raw_response_wrapper( + extract.batch, + ) + self.stream = to_raw_response_wrapper( + extract.stream, + ) + self.sync = to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithRawResponse: + return ResultsResourceWithRawResponse(self._extract.results) + + +class AsyncExtractResourceWithRawResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_raw_response_wrapper( + extract.async_, + ) + self.batch = async_to_raw_response_wrapper( + extract.batch, + ) + self.stream = async_to_raw_response_wrapper( + extract.stream, + ) + self.sync = async_to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithRawResponse: + return AsyncResultsResourceWithRawResponse(self._extract.results) + + +class ExtractResourceWithStreamingResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_streamed_response_wrapper( + extract.async_, + ) + self.batch = to_streamed_response_wrapper( + extract.batch, + ) + self.stream = to_streamed_response_wrapper( + extract.stream, + ) + self.sync = to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithStreamingResponse: + return ResultsResourceWithStreamingResponse(self._extract.results) + + +class AsyncExtractResourceWithStreamingResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_streamed_response_wrapper( + extract.async_, + ) + self.batch = async_to_streamed_response_wrapper( + extract.batch, + ) + self.stream = async_to_streamed_response_wrapper( + extract.stream, + ) + self.sync = async_to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithStreamingResponse: + return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py new file mode 100644 index 0000000..1533ac2 --- /dev/null +++ b/src/docstrange/resources/extract/results.py @@ -0,0 +1,294 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination +from ..._base_client import AsyncPaginator, make_request_options +from ...types.extract import result_list_params, result_retrieve_params +from ...types.extract_response import ExtractResponse + +__all__ = ["ResultsResource", "AsyncResultsResource"] + + +class ResultsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ResultsResourceWithStreamingResponse(self) + + def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + include_content: Include full extracted content (default: true) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPageNumberPagination[ExtractResponse]: + """ + List all extraction jobs for the authenticated user (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=SyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class AsyncResultsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncResultsResourceWithStreamingResponse(self) + + async def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + include_content: Include full extracted content (default: true) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return await self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: + """ + List all extraction jobs for the authenticated user (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=AsyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class ResultsResourceWithRawResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_raw_response_wrapper( + results.retrieve, + ) + self.list = to_raw_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithRawResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_raw_response_wrapper( + results.retrieve, + ) + self.list = async_to_raw_response_wrapper( + results.list, + ) + + +class ResultsResourceWithStreamingResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_streamed_response_wrapper( + results.retrieve, + ) + self.list = to_streamed_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithStreamingResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_streamed_response_wrapper( + results.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + results.list, + ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index fac3955..a18a10a 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,5 +4,18 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse +from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams +from .extraction_metadata import ExtractionMetadata as ExtractionMetadata +from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams +from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams +from .extract_batch_params import ExtractBatchParams as ExtractBatchParams +from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams +from .extract_stream_params import ExtractStreamParams as ExtractStreamParams +from .batch_extract_response import BatchExtractResponse as BatchExtractResponse +from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse +from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse +from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult +from .file_classification_result import FileClassificationResult as FileClassificationResult +from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py new file mode 100644 index 0000000..a36e5a8 --- /dev/null +++ b/src/docstrange/types/batch_classify_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .file_classification_result import FileClassificationResult + +__all__ = ["BatchClassifyResponse"] + + +class BatchClassifyResponse(BaseModel): + batch_id: str + + failed_files: int + + message: str + + results: List[FileClassificationResult] + + success: bool + + successful_files: int + + total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py new file mode 100644 index 0000000..4e73773 --- /dev/null +++ b/src/docstrange/types/batch_extract_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .extract_response import ExtractResponse + +__all__ = ["BatchExtractResponse"] + + +class BatchExtractResponse(BaseModel): + accepted_files: int + + batch_id: str + + message: str + + records: List[ExtractResponse] + + rejected_files: int + + success: bool + + total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py new file mode 100644 index 0000000..b7cde5c --- /dev/null +++ b/src/docstrange/types/chat_create_completion_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] + + +class ChatCreateCompletionParams(TypedDict, total=False): + messages: Required[Iterable[Message]] + """Chat messages array""" + + model: Required[str] + """Model identifier (e.g., nanonets/Nanonets-OCR-s)""" + + stream: bool + """Enable SSE streaming""" + + +class MessageContentFileURL(TypedDict, total=False): + url: str + + +class MessageContentImageURL(TypedDict, total=False): + url: str + + +class MessageContent(TypedDict, total=False): + file_url: MessageContentFileURL + + image_url: MessageContentImageURL + + text: str + + type: str + + +class Message(TypedDict, total=False): + content: Required[Iterable[MessageContent]] + """Message content as an array of parts (text or file/image URLs).""" + + role: Required[str] + """Message role (system, user, assistant)""" diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py new file mode 100644 index 0000000..5cac9e3 --- /dev/null +++ b/src/docstrange/types/classify_batch_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ClassifyBatchParams"] + + +class ClassifyBatchParams(TypedDict, total=False): + categories: Required[str] + """JSON array of category objects (max 50 categories)""" + + files: Required[SequenceNotStr[FileTypes]] + """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index 5bab3de..fdebbb0 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,6 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from .._models import BaseModel +from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -9,3 +12,5 @@ class ClassifyResponse(BaseModel): message: str success: bool + + result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py index ff11627..772427d 100644 --- a/src/docstrange/types/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -11,7 +11,10 @@ class ClassifySyncParams(TypedDict, total=False): categories: Required[str] - """JSON array of categories""" + """ + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] + """ file: Required[FileTypes] - """File to classify""" + """File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP)""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py new file mode 100644 index 0000000..387a007 --- /dev/null +++ b/src/docstrange/types/extract/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .pagination_info import PaginationInfo as PaginationInfo +from .result_list_params import ResultListParams as ResultListParams +from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams +from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py new file mode 100644 index 0000000..26a12e7 --- /dev/null +++ b/src/docstrange/types/extract/extraction_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .pagination_info import PaginationInfo +from ..extract_response import ExtractResponse + +__all__ = ["ExtractionListResponse"] + + +class ExtractionListResponse(BaseModel): + pagination: PaginationInfo + + results: List[ExtractResponse] + + success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py new file mode 100644 index 0000000..7a678a9 --- /dev/null +++ b/src/docstrange/types/extract/pagination_info.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["PaginationInfo"] + + +class PaginationInfo(BaseModel): + has_next: Optional[bool] = None + + has_previous: Optional[bool] = None + + page: Optional[int] = None + + page_size: Optional[int] = None + + total_count: Optional[int] = None + + total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py new file mode 100644 index 0000000..7dfe38a --- /dev/null +++ b/src/docstrange/types/extract/result_list_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ResultListParams"] + + +class ResultListParams(TypedDict, total=False): + page: int + + page_size: int + + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + + sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py new file mode 100644 index 0000000..539b80d --- /dev/null +++ b/src/docstrange/types/extract/result_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ResultRetrieveParams"] + + +class ResultRetrieveParams(TypedDict, total=False): + include_content: bool + """Include full extracted content (default: true)""" diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py new file mode 100644 index 0000000..428e085 --- /dev/null +++ b/src/docstrange/types/extract_async_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractAsyncParams"] + + +class ExtractAsyncParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + output_format: Required[str] + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple (e.g., `markdown,json`). + """ + + csv_options: str + """CSV extraction options (e.g., `table`)""" + + custom_instructions: str + """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" + + json_options: str + """JSON extraction options. + + Values: `hierarchy_output`, `table-of-contents`, field list + `["field1", "field2"]`, or JSON schema `{...}` + """ + + prompt_mode: Literal["append", "replace"] + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py new file mode 100644 index 0000000..bdc7b29 --- /dev/null +++ b/src/docstrange/types/extract_batch_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ExtractBatchParams"] + + +class ExtractBatchParams(TypedDict, total=False): + files: Required[SequenceNotStr[FileTypes]] + """Files to process (max 50)""" + + output_format: Required[str] + """Output format(s) for all files""" + + csv_options: str + + custom_instructions: str + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index ba021e0..9d93ead 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,17 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime +from typing_extensions import Literal from .._models import BaseModel +from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): + message: str + record_id: str + """Job ID for retrieving results""" - status: str + status: Literal["completed", "processing", "failed"] success: bool - message: Optional[str] = None + created_at: Optional[datetime] = None + + file_size: Optional[int] = None + """Size in bytes""" + + filename: Optional[str] = None + + output_format: Optional[str] = None + + pages_processed: Optional[int] = None + + processing_time: Optional[float] = None + """Time in seconds""" + + result: Optional[ExtractionResult] = None + """Results by format (only requested formats populated)""" diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py new file mode 100644 index 0000000..087ebc1 --- /dev/null +++ b/src/docstrange/types/extract_stream_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractStreamParams"] + + +class ExtractStreamParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + output_format: Required[str] + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple. + """ + + csv_options: str + """CSV extraction options""" + + custom_instructions: str + """Custom extraction instructions""" + + enable_streaming: bool + """Enable real-time streaming. + + If false, returns complete content via SSE batch mode. + """ + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" + + json_options: str + """JSON extraction options""" + + prompt_mode: Literal["append", "replace"] + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py new file mode 100644 index 0000000..333ff97 --- /dev/null +++ b/src/docstrange/types/extract_stream_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ExtractStreamResponse"] + +ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 242ce95..909fcea 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -11,7 +11,35 @@ class ExtractSyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload""" + """File to upload (PDF, Word, Excel, PowerPoint, images)""" output_format: Required[str] - """Output format""" + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple (e.g., `markdown,json`). + """ + + csv_options: str + """CSV extraction options (e.g., `table`)""" + + custom_instructions: str + """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" + + json_options: str + """JSON extraction options. + + Values: `hierarchy_output`, `table-of-contents`, field list + `["field1", "field2"]`, or JSON schema `{...}` + """ + + prompt_mode: Literal["append", "replace"] + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py new file mode 100644 index 0000000..579500c --- /dev/null +++ b/src/docstrange/types/extraction_format_result.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional + +from .._models import BaseModel +from .extraction_metadata import ExtractionMetadata + +__all__ = ["ExtractionFormatResult"] + + +class ExtractionFormatResult(BaseModel): + content: Union[str, object] + """Extracted content""" + + metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py new file mode 100644 index 0000000..9792ba0 --- /dev/null +++ b/src/docstrange/types/extraction_metadata.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExtractionMetadata"] + + +class ExtractionMetadata(BaseModel): + bounding_boxes: Optional[object] = None + """Element coordinates with page dimensions""" + + confidence_score: Optional[object] = None + """Confidence scores per field""" diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py new file mode 100644 index 0000000..57b2928 --- /dev/null +++ b/src/docstrange/types/extraction_result.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .extraction_format_result import ExtractionFormatResult + +__all__ = ["ExtractionResult"] + + +class ExtractionResult(BaseModel): + """Results by format (only requested formats populated)""" + + csv: Optional[ExtractionFormatResult] = None + + html: Optional[ExtractionFormatResult] = None + + json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) + + markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py new file mode 100644 index 0000000..397d924 --- /dev/null +++ b/src/docstrange/types/file_classification_result.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .page_classification import PageClassification + +__all__ = ["FileClassificationResult"] + + +class FileClassificationResult(BaseModel): + filename: str + + pages: List[PageClassification] + + total_pages: int + + error: Optional[str] = None + + processing_time: Optional[float] = None + """Time in seconds""" + + record_id: Optional[str] = None + """Record ID for polling results""" diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py new file mode 100644 index 0000000..2099524 --- /dev/null +++ b/src/docstrange/types/page_classification.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["PageClassification"] + + +class PageClassification(BaseModel): + category: str + """Matched category name or 'Other' if no match""" + + confidence: int + """Confidence score (0-100)""" + + page_number: int + + reasoning: str + """Explanation for the classification""" + + identified_category: Optional[str] = None + """ + Specific document type identified by the model (e.g., + 'Invoice_Amazon_Electronics'). Always provided - contains a more detailed + classification than the category. + """ diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/extract/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py new file mode 100644 index 0000000..ee1d5f5 --- /dev/null +++ b/tests/api_resources/extract/test_results.py @@ -0,0 +1,205 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type +from docstrange.types import ExtractResponse +from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResults: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Docstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Docstrange) -> None: + result = client.extract.results.list() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncResults: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + await async_client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py new file mode 100644 index 0000000..cbcd7c3 --- /dev/null +++ b/tests/api_resources/test_chat.py @@ -0,0 +1,183 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestChat: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "https://example.com/doc.pdf"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "file_url", + }, + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "Extract the text from the document.", + "type": "text", + }, + ], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + stream=False, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_completion(self, client: Docstrange) -> None: + response = client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_completion(self, client: Docstrange) -> None: + with client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncChat: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "https://example.com/doc.pdf"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "file_url", + }, + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "Extract the text from the document.", + "type": "text", + }, + ], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + stream=False, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: + response = await async_client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: + async with async_client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index 63539df..b4377f6 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse +from docstrange.types import ClassifyResponse, BatchClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,11 +17,48 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + classify = client.classify.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.classify.with_raw_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.classify.with_streaming_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: classify = client.classify.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -30,7 +67,7 @@ def test_method_sync(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.classify.with_raw_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -43,7 +80,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.classify.with_streaming_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: assert not response.is_closed @@ -60,11 +97,48 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + classify = await async_client.classify.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.classify.with_raw_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.classify.with_streaming_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: classify = await async_client.classify.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -73,7 +147,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.classify.with_raw_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -86,7 +160,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.classify.with_streaming_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index d8117c2..bd13f30 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,7 +9,10 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse +from docstrange.types import ( + ExtractResponse, + BatchExtractResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,12 +20,185 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_async(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_async(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream_with_all_params(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_raw_response_stream(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_streaming_response_stream(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: extract = client.extract.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_sync_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.sync( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -31,7 +207,7 @@ def test_method_sync(self, client: Docstrange) -> None: def test_raw_response_sync(self, client: Docstrange) -> None: response = client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) assert response.is_closed is True @@ -44,7 +220,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: def test_streaming_response_sync(self, client: Docstrange) -> None: with client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -60,12 +236,185 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.sync( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -74,7 +423,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) assert response.is_closed is True @@ -87,7 +436,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index 2756693..719c826 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,9 +862,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" - ).__enter__() + client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @@ -874,9 +872,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" - ).__enter__() + client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -905,7 +901,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") + response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -930,7 +926,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -955,7 +951,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1773,7 +1769,7 @@ async def test_retrying_timeout_errors_doesnt_leak( with pytest.raises(APITimeoutError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" + file=b"raw file contents", output_format="markdown" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1787,7 +1783,7 @@ async def test_retrying_status_errors_doesnt_leak( with pytest.raises(APIStatusError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" + file=b"raw file contents", output_format="markdown" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1817,7 +1813,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") + response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1842,7 +1838,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1867,7 +1863,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From a3d7c23e4f6027cb4eabb422a08aae1f1f4db511 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:32:12 +0000 Subject: [PATCH 15/26] feat(api): api update --- .stats.yml | 4 +- src/docstrange/resources/extract/extract.py | 44 ++++---------------- src/docstrange/types/extract_async_params.py | 11 ++--- src/docstrange/types/extract_sync_params.py | 11 ++--- tests/api_resources/test_extract.py | 8 ---- 5 files changed, 18 insertions(+), 60 deletions(-) diff --git a/.stats.yml b/.stats.yml index a5ffc81..ccaf55e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-1f31f87d4c6e168ffcccab94b8967a095195cac1ea2518bbddbeaf4611e02f05.yml -openapi_spec_hash: 18bec87d90926b0bc0cd98ce1d066d1f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-7f3f662020a40e0dbf7b19b724cf5c3e69f82246659477c1d71521c0c3cf62a0.yml +openapi_spec_hash: 53a8f7e0325845d16306ff7cb4272510 config_hash: c02183ee58f815311de9278705163d1a diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py index 1cca021..b83e01b 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -75,8 +75,6 @@ def async_( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -95,7 +93,8 @@ def async_( Recommended for large documents (>50 pages). Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) + file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use + file_url or file_base64. output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -104,10 +103,6 @@ def async_( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - file_base64: Base64-encoded file content - - file_url: URL to download file from - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field @@ -135,8 +130,6 @@ def async_( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -310,8 +303,6 @@ def sync( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -330,7 +321,8 @@ def sync( Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) + file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use + file_url or file_base64. output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -339,10 +331,6 @@ def sync( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - file_base64: Base64-encoded file content - - file_url: URL to download file from - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field @@ -370,8 +358,6 @@ def sync( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -416,8 +402,6 @@ async def async_( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -436,7 +420,8 @@ async def async_( Recommended for large documents (>50 pages). Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) + file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use + file_url or file_base64. output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -445,10 +430,6 @@ async def async_( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - file_base64: Base64-encoded file content - - file_url: URL to download file from - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field @@ -476,8 +457,6 @@ async def async_( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -651,8 +630,6 @@ async def sync( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -671,7 +648,8 @@ async def sync( Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) + file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use + file_url or file_base64. output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -680,10 +658,6 @@ async def sync( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - file_base64: Base64-encoded file content - - file_url: URL to download file from - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field @@ -711,8 +685,6 @@ async def sync( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py index 428e085..fd3f1b4 100644 --- a/src/docstrange/types/extract_async_params.py +++ b/src/docstrange/types/extract_async_params.py @@ -11,7 +11,10 @@ class ExtractAsyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" + """File to upload (PDF, Word, Excel, PowerPoint, images). + + Alternatively use file_url or file_base64. + """ output_format: Required[str] """Output format(s): `markdown`, `html`, `json`, `csv`. @@ -25,12 +28,6 @@ class ExtractAsyncParams(TypedDict, total=False): custom_instructions: str """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - include_metadata: str """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 909fcea..9ad55d7 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -11,7 +11,10 @@ class ExtractSyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" + """File to upload (PDF, Word, Excel, PowerPoint, images). + + Alternatively use file_url or file_base64. + """ output_format: Required[str] """Output format(s): `markdown`, `html`, `json`, `csv`. @@ -25,12 +28,6 @@ class ExtractSyncParams(TypedDict, total=False): custom_instructions: str """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - include_metadata: str """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index bd13f30..2046ac4 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -37,8 +37,6 @@ def test_method_async_with_all_params(self, client: Docstrange) -> None: output_format="markdown", csv_options="", custom_instructions="", - file_base64="", - file_url="", include_metadata="", json_options="", prompt_mode="append", @@ -194,8 +192,6 @@ def test_method_sync_with_all_params(self, client: Docstrange) -> None: output_format="markdown", csv_options="", custom_instructions="", - file_base64="", - file_url="", include_metadata="", json_options="", prompt_mode="append", @@ -253,8 +249,6 @@ async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) output_format="markdown", csv_options="", custom_instructions="", - file_base64="", - file_url="", include_metadata="", json_options="", prompt_mode="append", @@ -410,8 +404,6 @@ async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) output_format="markdown", csv_options="", custom_instructions="", - file_base64="", - file_url="", include_metadata="", json_options="", prompt_mode="append", From 9da26d4a173539f2a94608833b3002c5bf4d7f37 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:18:15 +0000 Subject: [PATCH 16/26] feat(api): api update --- .stats.yml | 6 +- README.md | 2 +- src/docstrange/resources/chat.py | 32 +---- src/docstrange/resources/extract/extract.py | 78 ++++++----- .../types/chat_create_completion_params.py | 2 +- src/docstrange/types/extract_async_params.py | 17 ++- src/docstrange/types/extract_stream_params.py | 5 +- src/docstrange/types/extract_sync_params.py | 17 ++- .../types/extraction_format_result.py | 6 +- src/docstrange/types/page_classification.py | 6 +- tests/api_resources/test_chat.py | 72 ++++------ tests/api_resources/test_classify.py | 24 ++-- tests/api_resources/test_extract.py | 128 ++++++++++-------- tests/test_client.py | 24 ++-- 14 files changed, 196 insertions(+), 223 deletions(-) diff --git a/.stats.yml b/.stats.yml index ccaf55e..0b3b4fc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-7f3f662020a40e0dbf7b19b724cf5c3e69f82246659477c1d71521c0c3cf62a0.yml -openapi_spec_hash: 53a8f7e0325845d16306ff7cb4272510 -config_hash: c02183ee58f815311de9278705163d1a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-d1e63191b34778603117f266822b2f6f73acbc657307949f2ed9bdb546ab1d88.yml +openapi_spec_hash: 06568800f2585c2fe3c6069cc9139d16 +config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 diff --git a/README.md b/README.md index 991dcba..994b399 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ client = Docstrange() client.extract.sync( file=Path("/path/to/file"), - output_format="markdown", + output_format="output_format", ) ``` diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py index 0a892c1..bd99e59 100644 --- a/src/docstrange/resources/chat.py +++ b/src/docstrange/resources/chat.py @@ -55,20 +55,8 @@ def create_completion( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """OpenAI-compatible chat completions endpoint. - - Supports text messages and - file/image inputs via `image_url` or `file_url` parts. - - **Limits and behavior:** - - - The backend enforces a model context limit of ~20,000 tokens (text + - multimodal tokens). - - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts - and embedded as data URLs. - - Inline data URLs are limited to ~64MB decoded size per item. - - For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + """ + OpenAI-compatible chat completions endpoint. Args: messages: Chat messages array @@ -135,20 +123,8 @@ async def create_completion( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: - """OpenAI-compatible chat completions endpoint. - - Supports text messages and - file/image inputs via `image_url` or `file_url` parts. - - **Limits and behavior:** - - - The backend enforces a model context limit of ~20,000 tokens (text + - multimodal tokens). - - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts - and embedded as data URLs. - - Inline data URLs are limited to ~64MB decoded size per item. - - For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + """ + OpenAI-compatible chat completions endpoint. Args: messages: Chat messages array diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py index b83e01b..46810ed 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -75,6 +75,8 @@ def async_( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -93,8 +95,7 @@ def async_( Recommended for large documents (>50 pages). Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use - file_url or file_base64. + file: File to upload (PDF, Word, Excel, PowerPoint, images) output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -103,10 +104,13 @@ def async_( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file_base64: Base64-encoded file content + + file_url: URL to download file from + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` + json_options: JSON extraction options. prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions @@ -130,6 +134,8 @@ def async_( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -225,14 +231,6 @@ def stream( Stream extraction results via Server-Sent Events (SSE) for real-time content delivery. - **Event Types:** - - - `content`: Incremental content chunks (streaming mode) - - `complete`: Full content at once (batch mode) - - `done`: Final event with record_id and processing_time - - `error`: Error information - - `async_queued`: Large files queued for async processing - Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: @@ -245,8 +243,7 @@ def stream( custom_instructions: Custom extraction instructions - enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch - mode. + enable_streaming: Enable real-time streaming. file_base64: Base64-encoded file content @@ -303,6 +300,8 @@ def sync( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -321,8 +320,7 @@ def sync( Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use - file_url or file_base64. + file: File to upload (PDF, Word, Excel, PowerPoint, images) output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -331,10 +329,13 @@ def sync( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file_base64: Base64-encoded file content + + file_url: URL to download file from + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` + json_options: JSON extraction options. prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions @@ -358,6 +359,8 @@ def sync( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -402,6 +405,8 @@ async def async_( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -420,8 +425,7 @@ async def async_( Recommended for large documents (>50 pages). Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use - file_url or file_base64. + file: File to upload (PDF, Word, Excel, PowerPoint, images) output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -430,10 +434,13 @@ async def async_( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file_base64: Base64-encoded file content + + file_url: URL to download file from + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` + json_options: JSON extraction options. prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions @@ -457,6 +464,8 @@ async def async_( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, @@ -552,14 +561,6 @@ async def stream( Stream extraction results via Server-Sent Events (SSE) for real-time content delivery. - **Event Types:** - - - `content`: Incremental content chunks (streaming mode) - - `complete`: Full content at once (batch mode) - - `done`: Final event with record_id and processing_time - - `error`: Error information - - `async_queued`: Large files queued for async processing - Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: @@ -572,8 +573,7 @@ async def stream( custom_instructions: Custom extraction instructions - enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch - mode. + enable_streaming: Enable real-time streaming. file_base64: Base64-encoded file content @@ -630,6 +630,8 @@ async def sync( output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, include_metadata: str | Omit = omit, json_options: str | Omit = omit, prompt_mode: Literal["append", "replace"] | Omit = omit, @@ -648,8 +650,7 @@ async def sync( Provide exactly one of: `file`, `file_url`, or `file_base64`. Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images). Alternatively use - file_url or file_base64. + file: File to upload (PDF, Word, Excel, PowerPoint, images) output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple (e.g., `markdown,json`). @@ -658,10 +659,13 @@ async def sync( custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file_base64: Base64-encoded file content + + file_url: URL to download file from + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field - list `["field1", "field2"]`, or JSON schema `{...}` + json_options: JSON extraction options. prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions @@ -685,6 +689,8 @@ async def sync( "output_format": output_format, "csv_options": csv_options, "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, "include_metadata": include_metadata, "json_options": json_options, "prompt_mode": prompt_mode, diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py index b7cde5c..a4a15e7 100644 --- a/src/docstrange/types/chat_create_completion_params.py +++ b/src/docstrange/types/chat_create_completion_params.py @@ -39,7 +39,7 @@ class MessageContent(TypedDict, total=False): class Message(TypedDict, total=False): content: Required[Iterable[MessageContent]] - """Message content as an array of parts (text or file/image URLs).""" + """Message content as an array of parts.""" role: Required[str] """Message role (system, user, assistant)""" diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py index fd3f1b4..364ee01 100644 --- a/src/docstrange/types/extract_async_params.py +++ b/src/docstrange/types/extract_async_params.py @@ -11,10 +11,7 @@ class ExtractAsyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images). - - Alternatively use file_url or file_base64. - """ + """File to upload (PDF, Word, Excel, PowerPoint, images)""" output_format: Required[str] """Output format(s): `markdown`, `html`, `json`, `csv`. @@ -28,15 +25,17 @@ class ExtractAsyncParams(TypedDict, total=False): custom_instructions: str """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + include_metadata: str """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" json_options: str - """JSON extraction options. - - Values: `hierarchy_output`, `table-of-contents`, field list - `["field1", "field2"]`, or JSON schema `{...}` - """ + """JSON extraction options.""" prompt_mode: Literal["append", "replace"] """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py index 087ebc1..68ee9a6 100644 --- a/src/docstrange/types/extract_stream_params.py +++ b/src/docstrange/types/extract_stream_params.py @@ -26,10 +26,7 @@ class ExtractStreamParams(TypedDict, total=False): """Custom extraction instructions""" enable_streaming: bool - """Enable real-time streaming. - - If false, returns complete content via SSE batch mode. - """ + """Enable real-time streaming.""" file_base64: str """Base64-encoded file content""" diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 9ad55d7..78813f6 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -11,10 +11,7 @@ class ExtractSyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images). - - Alternatively use file_url or file_base64. - """ + """File to upload (PDF, Word, Excel, PowerPoint, images)""" output_format: Required[str] """Output format(s): `markdown`, `html`, `json`, `csv`. @@ -28,15 +25,17 @@ class ExtractSyncParams(TypedDict, total=False): custom_instructions: str """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + include_metadata: str """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" json_options: str - """JSON extraction options. - - Values: `hierarchy_output`, `table-of-contents`, field list - `["field1", "field2"]`, or JSON schema `{...}` - """ + """JSON extraction options.""" prompt_mode: Literal["append", "replace"] """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py index 579500c..bfa97a9 100644 --- a/src/docstrange/types/extraction_format_result.py +++ b/src/docstrange/types/extraction_format_result.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union, Optional +from typing import Optional from .._models import BaseModel from .extraction_metadata import ExtractionMetadata @@ -9,7 +9,7 @@ class ExtractionFormatResult(BaseModel): - content: Union[str, object] - """Extracted content""" + content: object + """Extracted content (string or object)""" metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py index 2099524..62a2338 100644 --- a/src/docstrange/types/page_classification.py +++ b/src/docstrange/types/page_classification.py @@ -20,8 +20,4 @@ class PageClassification(BaseModel): """Explanation for the classification""" identified_category: Optional[str] = None - """ - Specific document type identified by the model (e.g., - 'Invoice_Amazon_Electronics'). Always provided - contains a more detailed - classification than the category. - """ + """Specific document type identified by the model.""" diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py index cbcd7c3..84bfced 100644 --- a/tests/api_resources/test_chat.py +++ b/tests/api_resources/test_chat.py @@ -22,11 +22,11 @@ def test_method_create_completion(self, client: Docstrange) -> None: chat = client.chat.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) assert_matches_type(object, chat, path=["response"]) @@ -37,24 +37,18 @@ def test_method_create_completion_with_all_params(self, client: Docstrange) -> N messages=[ { "content": [ - { - "file_url": {"url": "https://example.com/doc.pdf"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "file_url", - }, { "file_url": {"url": "url"}, "image_url": {"url": "url"}, - "text": "Extract the text from the document.", - "type": "text", - }, + "text": "text", + "type": "type", + } ], - "role": "user", + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", - stream=False, + model="model", + stream=True, ) assert_matches_type(object, chat, path=["response"]) @@ -64,11 +58,11 @@ def test_raw_response_create_completion(self, client: Docstrange) -> None: response = client.chat.with_raw_response.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) assert response.is_closed is True @@ -82,11 +76,11 @@ def test_streaming_response_create_completion(self, client: Docstrange) -> None: with client.chat.with_streaming_response.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -108,11 +102,11 @@ async def test_method_create_completion(self, async_client: AsyncDocstrange) -> chat = await async_client.chat.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) assert_matches_type(object, chat, path=["response"]) @@ -123,24 +117,18 @@ async def test_method_create_completion_with_all_params(self, async_client: Asyn messages=[ { "content": [ - { - "file_url": {"url": "https://example.com/doc.pdf"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "file_url", - }, { "file_url": {"url": "url"}, "image_url": {"url": "url"}, - "text": "Extract the text from the document.", - "type": "text", - }, + "text": "text", + "type": "type", + } ], - "role": "user", + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", - stream=False, + model="model", + stream=True, ) assert_matches_type(object, chat, path=["response"]) @@ -150,11 +138,11 @@ async def test_raw_response_create_completion(self, async_client: AsyncDocstrang response = await async_client.chat.with_raw_response.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) assert response.is_closed is True @@ -168,11 +156,11 @@ async def test_streaming_response_create_completion(self, async_client: AsyncDoc async with async_client.chat.with_streaming_response.create_completion( messages=[ { - "content": [{}, {}], - "role": "user", + "content": [{}], + "role": "role", } ], - model="nanonets/Nanonets-OCR-s", + model="model", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index b4377f6..d40e1fa 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -21,7 +21,7 @@ class TestClassify: @parametrize def test_method_batch(self, client: Docstrange) -> None: classify = client.classify.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @@ -30,7 +30,7 @@ def test_method_batch(self, client: Docstrange) -> None: @parametrize def test_raw_response_batch(self, client: Docstrange) -> None: response = client.classify.with_raw_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) @@ -43,7 +43,7 @@ def test_raw_response_batch(self, client: Docstrange) -> None: @parametrize def test_streaming_response_batch(self, client: Docstrange) -> None: with client.classify.with_streaming_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) as response: assert not response.is_closed @@ -58,7 +58,7 @@ def test_streaming_response_batch(self, client: Docstrange) -> None: @parametrize def test_method_sync(self, client: Docstrange) -> None: classify = client.classify.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -67,7 +67,7 @@ def test_method_sync(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.classify.with_raw_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) @@ -80,7 +80,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.classify.with_streaming_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) as response: assert not response.is_closed @@ -101,7 +101,7 @@ class TestAsyncClassify: @parametrize async def test_method_batch(self, async_client: AsyncDocstrange) -> None: classify = await async_client.classify.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) assert_matches_type(BatchClassifyResponse, classify, path=["response"]) @@ -110,7 +110,7 @@ async def test_method_batch(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: response = await async_client.classify.with_raw_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) @@ -123,7 +123,7 @@ async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: async with async_client.classify.with_streaming_response.batch( - categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + categories="categories", files=[b"raw file contents"], ) as response: assert not response.is_closed @@ -138,7 +138,7 @@ async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: classify = await async_client.classify.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -147,7 +147,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.classify.with_raw_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) @@ -160,7 +160,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.classify.with_streaming_response.sync( - categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', + categories="categories", file=b"raw file contents", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index 2046ac4..4f68b18 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -25,7 +25,7 @@ class TestExtract: def test_method_async(self, client: Docstrange) -> None: extract = client.extract.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -34,11 +34,13 @@ def test_method_async(self, client: Docstrange) -> None: def test_method_async_with_all_params(self, client: Docstrange) -> None: extract = client.extract.async_( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - include_metadata="", - json_options="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -48,7 +50,7 @@ def test_method_async_with_all_params(self, client: Docstrange) -> None: def test_raw_response_async(self, client: Docstrange) -> None: response = client.extract.with_raw_response.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -61,7 +63,7 @@ def test_raw_response_async(self, client: Docstrange) -> None: def test_streaming_response_async(self, client: Docstrange) -> None: with client.extract.with_streaming_response.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -76,7 +78,7 @@ def test_streaming_response_async(self, client: Docstrange) -> None: def test_method_batch(self, client: Docstrange) -> None: extract = client.extract.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) assert_matches_type(BatchExtractResponse, extract, path=["response"]) @@ -85,7 +87,7 @@ def test_method_batch(self, client: Docstrange) -> None: def test_method_batch_with_all_params(self, client: Docstrange) -> None: extract = client.extract.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", csv_options="csv_options", custom_instructions="custom_instructions", include_metadata="include_metadata", @@ -99,7 +101,7 @@ def test_method_batch_with_all_params(self, client: Docstrange) -> None: def test_raw_response_batch(self, client: Docstrange) -> None: response = client.extract.with_raw_response.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -112,7 +114,7 @@ def test_raw_response_batch(self, client: Docstrange) -> None: def test_streaming_response_batch(self, client: Docstrange) -> None: with client.extract.with_streaming_response.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -127,7 +129,7 @@ def test_streaming_response_batch(self, client: Docstrange) -> None: def test_method_stream(self, client: Docstrange) -> None: extract_stream = client.extract.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) extract_stream.response.close() @@ -136,14 +138,14 @@ def test_method_stream(self, client: Docstrange) -> None: def test_method_stream_with_all_params(self, client: Docstrange) -> None: extract_stream = client.extract.stream( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) extract_stream.response.close() @@ -153,7 +155,7 @@ def test_method_stream_with_all_params(self, client: Docstrange) -> None: def test_raw_response_stream(self, client: Docstrange) -> None: response = client.extract.with_raw_response.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -165,7 +167,7 @@ def test_raw_response_stream(self, client: Docstrange) -> None: def test_streaming_response_stream(self, client: Docstrange) -> None: with client.extract.with_streaming_response.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -180,7 +182,7 @@ def test_streaming_response_stream(self, client: Docstrange) -> None: def test_method_sync(self, client: Docstrange) -> None: extract = client.extract.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -189,11 +191,13 @@ def test_method_sync(self, client: Docstrange) -> None: def test_method_sync_with_all_params(self, client: Docstrange) -> None: extract = client.extract.sync( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - include_metadata="", - json_options="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -203,7 +207,7 @@ def test_method_sync_with_all_params(self, client: Docstrange) -> None: def test_raw_response_sync(self, client: Docstrange) -> None: response = client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -216,7 +220,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: def test_streaming_response_sync(self, client: Docstrange) -> None: with client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -237,7 +241,7 @@ class TestAsyncExtract: async def test_method_async(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -246,11 +250,13 @@ async def test_method_async(self, async_client: AsyncDocstrange) -> None: async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.async_( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - include_metadata="", - json_options="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -260,7 +266,7 @@ async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -273,7 +279,7 @@ async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.async_( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -288,7 +294,7 @@ async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> async def test_method_batch(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) assert_matches_type(BatchExtractResponse, extract, path=["response"]) @@ -297,7 +303,7 @@ async def test_method_batch(self, async_client: AsyncDocstrange) -> None: async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", csv_options="csv_options", custom_instructions="custom_instructions", include_metadata="include_metadata", @@ -311,7 +317,7 @@ async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -324,7 +330,7 @@ async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.batch( files=[b"raw file contents"], - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -339,7 +345,7 @@ async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> async def test_method_stream(self, async_client: AsyncDocstrange) -> None: extract_stream = await async_client.extract.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) await extract_stream.response.aclose() @@ -348,14 +354,14 @@ async def test_method_stream(self, async_client: AsyncDocstrange) -> None: async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: extract_stream = await async_client.extract.stream( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) await extract_stream.response.aclose() @@ -365,7 +371,7 @@ async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -377,7 +383,7 @@ async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.stream( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -392,7 +398,7 @@ async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> async def test_method_sync(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -401,11 +407,13 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - include_metadata="", - json_options="", + output_format="output_format", + csv_options="csv_options", + custom_instructions="custom_instructions", + file_base64="file_base64", + file_url="file_url", + include_metadata="include_metadata", + json_options="json_options", prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -415,7 +423,7 @@ async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) assert response.is_closed is True @@ -428,7 +436,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="markdown", + output_format="output_format", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index 719c826..2756693 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,7 +862,9 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() + client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__enter__() assert _get_open_connections(client) == 0 @@ -872,7 +874,9 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() + client.extract.with_streaming_response.sync( + file=b"raw file contents", output_format="output_format" + ).__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -901,7 +905,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") + response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -926,7 +930,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -951,7 +955,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1769,7 +1773,7 @@ async def test_retrying_timeout_errors_doesnt_leak( with pytest.raises(APITimeoutError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown" + file=b"raw file contents", output_format="output_format" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1783,7 +1787,7 @@ async def test_retrying_status_errors_doesnt_leak( with pytest.raises(APIStatusError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown" + file=b"raw file contents", output_format="output_format" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1813,7 +1817,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") + response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1838,7 +1842,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1863,7 +1867,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 9cfaf51d7c188ab63fc6352d6a689e5c0a8dbe58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:20:46 +0000 Subject: [PATCH 17/26] feat(api): api update --- .stats.yml | 6 +- README.md | 61 -- api.md | 50 +- src/docstrange/_client.py | 39 +- src/docstrange/resources/__init__.py | 14 - src/docstrange/resources/chat.py | 194 ------ src/docstrange/resources/classify.py | 175 +---- src/docstrange/resources/extract/__init__.py | 14 - src/docstrange/resources/extract/extract.py | 649 +----------------- src/docstrange/resources/extract/results.py | 294 -------- src/docstrange/types/__init__.py | 13 - .../types/batch_classify_response.py | 24 - .../types/batch_extract_response.py | 24 - .../types/chat_create_completion_params.py | 45 -- src/docstrange/types/classify_batch_params.py | 17 - src/docstrange/types/classify_response.py | 5 - src/docstrange/types/classify_sync_params.py | 7 +- src/docstrange/types/extract/__init__.py | 5 - .../types/extract/extraction_list_response.py | 17 - .../types/extract/pagination_info.py | 21 - .../types/extract/result_list_params.py | 17 - .../types/extract/result_retrieve_params.py | 12 - src/docstrange/types/extract_async_params.py | 41 -- src/docstrange/types/extract_batch_params.py | 27 - src/docstrange/types/extract_response.py | 25 +- src/docstrange/types/extract_stream_params.py | 44 -- .../types/extract_stream_response.py | 7 - src/docstrange/types/extract_sync_params.py | 30 +- .../types/extraction_format_result.py | 15 - src/docstrange/types/extraction_metadata.py | 15 - src/docstrange/types/extraction_result.py | 22 - .../types/file_classification_result.py | 24 - src/docstrange/types/page_classification.py | 23 - tests/api_resources/extract/test_results.py | 205 ------ tests/api_resources/test_chat.py | 171 ----- tests/api_resources/test_classify.py | 76 +- tests/api_resources/test_extract.py | 351 +--------- 37 files changed, 72 insertions(+), 2707 deletions(-) delete mode 100644 src/docstrange/resources/chat.py delete mode 100644 src/docstrange/resources/extract/results.py delete mode 100644 src/docstrange/types/batch_classify_response.py delete mode 100644 src/docstrange/types/batch_extract_response.py delete mode 100644 src/docstrange/types/chat_create_completion_params.py delete mode 100644 src/docstrange/types/classify_batch_params.py delete mode 100644 src/docstrange/types/extract/extraction_list_response.py delete mode 100644 src/docstrange/types/extract/pagination_info.py delete mode 100644 src/docstrange/types/extract/result_list_params.py delete mode 100644 src/docstrange/types/extract/result_retrieve_params.py delete mode 100644 src/docstrange/types/extract_async_params.py delete mode 100644 src/docstrange/types/extract_batch_params.py delete mode 100644 src/docstrange/types/extract_stream_params.py delete mode 100644 src/docstrange/types/extract_stream_response.py delete mode 100644 src/docstrange/types/extraction_format_result.py delete mode 100644 src/docstrange/types/extraction_metadata.py delete mode 100644 src/docstrange/types/extraction_result.py delete mode 100644 src/docstrange/types/file_classification_result.py delete mode 100644 src/docstrange/types/page_classification.py delete mode 100644 tests/api_resources/extract/test_results.py delete mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 0b3b4fc..1d97927 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-d1e63191b34778603117f266822b2f6f73acbc657307949f2ed9bdb546ab1d88.yml -openapi_spec_hash: 06568800f2585c2fe3c6069cc9139d16 +configured_endpoints: 2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-3843cc83cc328a1c86126018a4309fbcd13aac4df5fd9afefd652e21e59530ab.yml +openapi_spec_hash: b4046d95714426b15edb8ac003f904c6 config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 diff --git a/README.md b/README.md index 994b399..622faec 100644 --- a/README.md +++ b/README.md @@ -115,67 +115,6 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## Pagination - -List methods in the Docstrange API are paginated. - -This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: - -```python -from docstrange import Docstrange - -client = Docstrange() - -all_results = [] -# Automatically fetches more pages as needed. -for result in client.extract.results.list(): - # Do something with result here - all_results.append(result) -print(all_results) -``` - -Or, asynchronously: - -```python -import asyncio -from docstrange import AsyncDocstrange - -client = AsyncDocstrange() - - -async def main() -> None: - all_results = [] - # Iterate through items across all pages, issuing requests as needed. - async for result in client.extract.results.list(): - all_results.append(result) - print(all_results) - - -asyncio.run(main()) -``` - -Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: - -```python -first_page = await client.extract.results.list() -if first_page.has_next_page(): - print(f"will fetch next page using these details: {first_page.next_page_info()}") - next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.results)}") - -# Remove `await` for non-async usage. -``` - -Or just work directly with the returned data: - -```python -first_page = await client.extract.results.list() -for result in first_page.results: - print(result.record_id) - -# Remove `await` for non-async usage. -``` - ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 7ccbce8..90358a6 100644 --- a/api.md +++ b/api.md @@ -3,67 +3,21 @@ Types: ```python -from docstrange.types import ( - BatchExtractRequestBody, - BatchExtractResponse, - ExtractRequestBody, - ExtractResponse, - ExtractionFormatResult, - ExtractionMetadata, - ExtractionResult, - StreamExtractRequestBody, - ExtractStreamResponse, -) +from docstrange.types import ExtractResponse ``` Methods: -- client.extract.async\_(\*\*params) -> ExtractResponse -- client.extract.batch(\*\*params) -> BatchExtractResponse -- client.extract.stream(\*\*params) -> str - client.extract.sync(\*\*params) -> ExtractResponse -## Results - -Types: - -```python -from docstrange.types.extract import ExtractionListResponse, PaginationInfo -``` - -Methods: - -- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] - # Classify Types: ```python -from docstrange.types import ( - BatchClassifyRequestBody, - BatchClassifyResponse, - ClassifyRequestBody, - ClassifyResponse, - FileClassificationResult, - PageClassification, -) +from docstrange.types import ClassifyResponse ``` Methods: -- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse - -# Chat - -Types: - -```python -from docstrange.types import ChatCompletionsRequest -``` - -Methods: - -- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 4c5bcc4..271a014 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,8 +31,7 @@ ) if TYPE_CHECKING: - from .resources import chat, extract, classify - from .resources.chat import ChatResource, AsyncChatResource + from .resources import extract, classify from .resources.classify import ClassifyResource, AsyncClassifyResource from .resources.extract.extract import ExtractResource, AsyncExtractResource @@ -115,12 +114,6 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) - @cached_property - def chat(self) -> ChatResource: - from .resources.chat import ChatResource - - return ChatResource(self) - @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -301,12 +294,6 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) - @cached_property - def chat(self) -> AsyncChatResource: - from .resources.chat import AsyncChatResource - - return AsyncChatResource(self) - @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -438,12 +425,6 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithRawResponse: - from .resources.chat import ChatResourceWithRawResponse - - return ChatResourceWithRawResponse(self._client.chat) - class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -463,12 +444,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithRawResponse: - from .resources.chat import AsyncChatResourceWithRawResponse - - return AsyncChatResourceWithRawResponse(self._client.chat) - class DocstrangeWithStreamedResponse: _client: Docstrange @@ -488,12 +463,6 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithStreamingResponse: - from .resources.chat import ChatResourceWithStreamingResponse - - return ChatResourceWithStreamingResponse(self._client.chat) - class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -513,12 +482,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: - from .resources.chat import AsyncChatResourceWithStreamingResponse - - return AsyncChatResourceWithStreamingResponse(self._client.chat) - Client = Docstrange diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index a3172dd..6d7ff55 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) from .extract import ( ExtractResource, AsyncExtractResource, @@ -38,10 +30,4 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py deleted file mode 100644 index bd99e59..0000000 --- a/src/docstrange/resources/chat.py +++ /dev/null @@ -1,194 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable - -import httpx - -from ..types import chat_create_completion_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - messages: Chat messages array - - model: Model identifier (e.g., nanonets/Nanonets-OCR-s) - - stream: Enable SSE streaming - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/chat/completions", - body=maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - messages: Chat messages array - - model: Model identifier (e.g., nanonets/Nanonets-OCR-s) - - stream: Enable SSE streaming - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/chat/completions", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_raw_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_raw_response_wrapper( - chat.create_completion, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_streamed_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_streamed_response_wrapper( - chat.create_completion, - ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index 5476a59..69045f4 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,11 +2,13 @@ from __future__ import annotations +from typing import Mapping, cast + import httpx -from ..types import classify_sync_params, classify_batch_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given -from .._utils import maybe_transform, async_maybe_transform +from ..types import classify_sync_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,7 +19,6 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse -from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -42,56 +43,6 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) - def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50 files). - - Files are queued for background processing. Use - `GET /api/v1/extract/results/{record_id}` to poll results. - - Args: - categories: JSON array of category objects (max 50 categories) - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/classify/batch", - body=maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - def sync( self, *, @@ -105,17 +56,12 @@ def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document into predefined categories synchronously. - - Each page is classified individually with category, confidence score (0-100), - and reasoning. + Classify a single document. Args: - categories: - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] + categories: JSON array of categories - file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) + file: File to classify extra_headers: Send extra headers @@ -125,19 +71,21 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -165,56 +113,6 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) - async def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50 files). - - Files are queued for background processing. Use - `GET /api/v1/extract/results/{record_id}` to poll results. - - Args: - categories: JSON array of category objects (max 50 categories) - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/classify/batch", - body=await async_maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - async def sync( self, *, @@ -228,17 +126,12 @@ async def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document into predefined categories synchronously. - - Each page is classified individually with category, confidence score (0-100), - and reasoning. + Classify a single document. Args: - categories: - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] + categories: JSON array of categories - file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) + file: File to classify extra_headers: Send extra headers @@ -248,19 +141,21 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -272,9 +167,6 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_raw_response_wrapper( - classify.batch, - ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -284,9 +176,6 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_raw_response_wrapper( - classify.batch, - ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -296,9 +185,6 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_streamed_response_wrapper( - classify.batch, - ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -308,9 +194,6 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_streamed_response_wrapper( - classify.batch, - ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py index 22cd257..a0fee27 100644 --- a/src/docstrange/resources/extract/__init__.py +++ b/src/docstrange/resources/extract/__init__.py @@ -8,22 +8,8 @@ ExtractResourceWithStreamingResponse, AsyncExtractResourceWithStreamingResponse, ) -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) __all__ = [ - "ResultsResource", - "AsyncResultsResource", - "ResultsResourceWithRawResponse", - "AsyncResultsResourceWithRawResponse", - "ResultsResourceWithStreamingResponse", - "AsyncResultsResourceWithStreamingResponse", "ExtractResource", "AsyncExtractResource", "ExtractResourceWithRawResponse", diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py index 46810ed..0fe0d14 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -2,31 +2,13 @@ from __future__ import annotations -from typing_extensions import Literal +from typing import Mapping, cast import httpx -from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) -from ..._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) -from ..._utils import maybe_transform, async_maybe_transform +from ...types import extract_sync_params +from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -35,20 +17,13 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._streaming import Stream, AsyncStream from ..._base_client import make_request_options from ...types.extract_response import ExtractResponse -from ...types.batch_extract_response import BatchExtractResponse -from ...types.extract_stream_response import ExtractStreamResponse __all__ = ["ExtractResource", "AsyncExtractResource"] class ExtractResource(SyncAPIResource): - @cached_property - def results(self) -> ResultsResource: - return ResultsResource(self._client) - @cached_property def with_raw_response(self) -> ExtractResourceWithRawResponse: """ @@ -68,18 +43,11 @@ def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: """ return ExtractResourceWithStreamingResponse(self) - def async_( + def sync( self, *, file: FileTypes, output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -87,93 +55,13 @@ def async_( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExtractResponse: - """Queue a document for background processing. - - Returns a `record_id` to poll - results via `GET /api/v1/extract/results/{record_id}`. - - Recommended for large documents (>50 pages). - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/async", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """Process multiple files asynchronously (max 50 files). - - All files share the same - extraction options. + Process a document and return extracted content immediately. Args: - files: Files to process (max 50) + file: File to upload - output_format: Output format(s) for all files + output_format: Output format extra_headers: Send extra headers @@ -183,190 +71,21 @@ def batch( timeout: Override the client-level default timeout for this request, in seconds """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/batch", - body=maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + } ) - - def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[ExtractStreamResponse]: - """ - Stream extraction results via Server-Sent Events (SSE) for real-time content - delivery. - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for - multiple. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - enable_streaming: Enable real-time streaming. - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/stream", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=Stream[ExtractStreamResponse], - ) - - def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Process a document and return extracted content immediately. - - (Works for files - with 5 pages or less.) - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/extract/sync", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), + body=maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -375,10 +94,6 @@ def sync( class AsyncExtractResource(AsyncAPIResource): - @cached_property - def results(self) -> AsyncResultsResource: - return AsyncResultsResource(self._client) - @cached_property def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: """ @@ -398,18 +113,11 @@ def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: """ return AsyncExtractResourceWithStreamingResponse(self) - async def async_( + async def sync( self, *, file: FileTypes, output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -417,93 +125,13 @@ async def async_( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExtractResponse: - """Queue a document for background processing. - - Returns a `record_id` to poll - results via `GET /api/v1/extract/results/{record_id}`. - - Recommended for large documents (>50 pages). - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/async", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - async def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """Process multiple files asynchronously (max 50 files). - - All files share the same - extraction options. + Process a document and return extracted content immediately. Args: - files: Files to process (max 50) + file: File to upload - output_format: Output format(s) for all files + output_format: Output format extra_headers: Send extra headers @@ -513,190 +141,21 @@ async def batch( timeout: Override the client-level default timeout for this request, in seconds """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/batch", - body=await async_maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + } ) - - async def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[ExtractStreamResponse]: - """ - Stream extraction results via Server-Sent Events (SSE) for real-time content - delivery. - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for - multiple. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - enable_streaming: Enable real-time streaming. - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/stream", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=AsyncStream[ExtractStreamResponse], - ) - - async def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """Process a document and return extracted content immediately. - - (Works for files - with 5 pages or less.) - - Provide exactly one of: `file`, `file_url`, or `file_base64`. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple - (e.g., `markdown,json`). - - csv_options: CSV extraction options (e.g., `table`) - - custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` - - json_options: JSON extraction options. - - prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/extract/sync", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), + body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -708,85 +167,33 @@ class ExtractResourceWithRawResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract - self.async_ = to_raw_response_wrapper( - extract.async_, - ) - self.batch = to_raw_response_wrapper( - extract.batch, - ) - self.stream = to_raw_response_wrapper( - extract.stream, - ) self.sync = to_raw_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> ResultsResourceWithRawResponse: - return ResultsResourceWithRawResponse(self._extract.results) - class AsyncExtractResourceWithRawResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract - self.async_ = async_to_raw_response_wrapper( - extract.async_, - ) - self.batch = async_to_raw_response_wrapper( - extract.batch, - ) - self.stream = async_to_raw_response_wrapper( - extract.stream, - ) self.sync = async_to_raw_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> AsyncResultsResourceWithRawResponse: - return AsyncResultsResourceWithRawResponse(self._extract.results) - class ExtractResourceWithStreamingResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract - self.async_ = to_streamed_response_wrapper( - extract.async_, - ) - self.batch = to_streamed_response_wrapper( - extract.batch, - ) - self.stream = to_streamed_response_wrapper( - extract.stream, - ) self.sync = to_streamed_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> ResultsResourceWithStreamingResponse: - return ResultsResourceWithStreamingResponse(self._extract.results) - class AsyncExtractResourceWithStreamingResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract - self.async_ = async_to_streamed_response_wrapper( - extract.async_, - ) - self.batch = async_to_streamed_response_wrapper( - extract.batch, - ) - self.stream = async_to_streamed_response_wrapper( - extract.stream, - ) self.sync = async_to_streamed_response_wrapper( extract.sync, ) - - @cached_property - def results(self) -> AsyncResultsResourceWithStreamingResponse: - return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py deleted file mode 100644 index 1533ac2..0000000 --- a/src/docstrange/resources/extract/results.py +++ /dev/null @@ -1,294 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination -from ..._base_client import AsyncPaginator, make_request_options -from ...types.extract import result_list_params, result_retrieve_params -from ...types.extract_response import ExtractResponse - -__all__ = ["ResultsResource", "AsyncResultsResource"] - - -class ResultsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ResultsResourceWithStreamingResponse(self) - - def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - include_content: Include full extracted content (default: true) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageNumberPagination[ExtractResponse]: - """ - List all extraction jobs for the authenticated user (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=SyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class AsyncResultsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncResultsResourceWithStreamingResponse(self) - - async def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - include_content: Include full extracted content (default: true) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return await self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: - """ - List all extraction jobs for the authenticated user (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=AsyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class ResultsResourceWithRawResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_raw_response_wrapper( - results.retrieve, - ) - self.list = to_raw_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithRawResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_raw_response_wrapper( - results.retrieve, - ) - self.list = async_to_raw_response_wrapper( - results.list, - ) - - -class ResultsResourceWithStreamingResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_streamed_response_wrapper( - results.retrieve, - ) - self.list = to_streamed_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithStreamingResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_streamed_response_wrapper( - results.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - results.list, - ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index a18a10a..fac3955 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,18 +4,5 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse -from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams -from .extraction_metadata import ExtractionMetadata as ExtractionMetadata -from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams -from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams -from .extract_batch_params import ExtractBatchParams as ExtractBatchParams -from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams -from .extract_stream_params import ExtractStreamParams as ExtractStreamParams -from .batch_extract_response import BatchExtractResponse as BatchExtractResponse -from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse -from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse -from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult -from .file_classification_result import FileClassificationResult as FileClassificationResult -from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py deleted file mode 100644 index a36e5a8..0000000 --- a/src/docstrange/types/batch_classify_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .file_classification_result import FileClassificationResult - -__all__ = ["BatchClassifyResponse"] - - -class BatchClassifyResponse(BaseModel): - batch_id: str - - failed_files: int - - message: str - - results: List[FileClassificationResult] - - success: bool - - successful_files: int - - total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py deleted file mode 100644 index 4e73773..0000000 --- a/src/docstrange/types/batch_extract_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .extract_response import ExtractResponse - -__all__ = ["BatchExtractResponse"] - - -class BatchExtractResponse(BaseModel): - accepted_files: int - - batch_id: str - - message: str - - records: List[ExtractResponse] - - rejected_files: int - - success: bool - - total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py deleted file mode 100644 index a4a15e7..0000000 --- a/src/docstrange/types/chat_create_completion_params.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, TypedDict - -__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] - - -class ChatCreateCompletionParams(TypedDict, total=False): - messages: Required[Iterable[Message]] - """Chat messages array""" - - model: Required[str] - """Model identifier (e.g., nanonets/Nanonets-OCR-s)""" - - stream: bool - """Enable SSE streaming""" - - -class MessageContentFileURL(TypedDict, total=False): - url: str - - -class MessageContentImageURL(TypedDict, total=False): - url: str - - -class MessageContent(TypedDict, total=False): - file_url: MessageContentFileURL - - image_url: MessageContentImageURL - - text: str - - type: str - - -class Message(TypedDict, total=False): - content: Required[Iterable[MessageContent]] - """Message content as an array of parts.""" - - role: Required[str] - """Message role (system, user, assistant)""" diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py deleted file mode 100644 index 5cac9e3..0000000 --- a/src/docstrange/types/classify_batch_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ClassifyBatchParams"] - - -class ClassifyBatchParams(TypedDict, total=False): - categories: Required[str] - """JSON array of category objects (max 50 categories)""" - - files: Required[SequenceNotStr[FileTypes]] - """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index fdebbb0..5bab3de 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,9 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional - from .._models import BaseModel -from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -12,5 +9,3 @@ class ClassifyResponse(BaseModel): message: str success: bool - - result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py index 772427d..ff11627 100644 --- a/src/docstrange/types/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -11,10 +11,7 @@ class ClassifySyncParams(TypedDict, total=False): categories: Required[str] - """ - JSON array of category objects: [{"name": "Category Name", "description": - "Optional description"}] - """ + """JSON array of categories""" file: Required[FileTypes] - """File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP)""" + """File to classify""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py index 387a007..f8ee8b1 100644 --- a/src/docstrange/types/extract/__init__.py +++ b/src/docstrange/types/extract/__init__.py @@ -1,8 +1,3 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations - -from .pagination_info import PaginationInfo as PaginationInfo -from .result_list_params import ResultListParams as ResultListParams -from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams -from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py deleted file mode 100644 index 26a12e7..0000000 --- a/src/docstrange/types/extract/extraction_list_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel -from .pagination_info import PaginationInfo -from ..extract_response import ExtractResponse - -__all__ = ["ExtractionListResponse"] - - -class ExtractionListResponse(BaseModel): - pagination: PaginationInfo - - results: List[ExtractResponse] - - success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py deleted file mode 100644 index 7a678a9..0000000 --- a/src/docstrange/types/extract/pagination_info.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["PaginationInfo"] - - -class PaginationInfo(BaseModel): - has_next: Optional[bool] = None - - has_previous: Optional[bool] = None - - page: Optional[int] = None - - page_size: Optional[int] = None - - total_count: Optional[int] = None - - total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py deleted file mode 100644 index 7dfe38a..0000000 --- a/src/docstrange/types/extract/result_list_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, TypedDict - -__all__ = ["ResultListParams"] - - -class ResultListParams(TypedDict, total=False): - page: int - - page_size: int - - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - - sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py deleted file mode 100644 index 539b80d..0000000 --- a/src/docstrange/types/extract/result_retrieve_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["ResultRetrieveParams"] - - -class ResultRetrieveParams(TypedDict, total=False): - include_content: bool - """Include full extracted content (default: true)""" diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py deleted file mode 100644 index 364ee01..0000000 --- a/src/docstrange/types/extract_async_params.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractAsyncParams"] - - -class ExtractAsyncParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple (e.g., `markdown,json`). - """ - - csv_options: str - """CSV extraction options (e.g., `table`)""" - - custom_instructions: str - """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options.""" - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py deleted file mode 100644 index bdc7b29..0000000 --- a/src/docstrange/types/extract_batch_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ExtractBatchParams"] - - -class ExtractBatchParams(TypedDict, total=False): - files: Required[SequenceNotStr[FileTypes]] - """Files to process (max 50)""" - - output_format: Required[str] - """Output format(s) for all files""" - - csv_options: str - - custom_instructions: str - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index 9d93ead..ba021e0 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,38 +1,17 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional -from datetime import datetime -from typing_extensions import Literal from .._models import BaseModel -from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): - message: str - record_id: str - """Job ID for retrieving results""" - status: Literal["completed", "processing", "failed"] + status: str success: bool - created_at: Optional[datetime] = None - - file_size: Optional[int] = None - """Size in bytes""" - - filename: Optional[str] = None - - output_format: Optional[str] = None - - pages_processed: Optional[int] = None - - processing_time: Optional[float] = None - """Time in seconds""" - - result: Optional[ExtractionResult] = None - """Results by format (only requested formats populated)""" + message: Optional[str] = None diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py deleted file mode 100644 index 68ee9a6..0000000 --- a/src/docstrange/types/extract_stream_params.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractStreamParams"] - - -class ExtractStreamParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple. - """ - - csv_options: str - """CSV extraction options""" - - custom_instructions: str - """Custom extraction instructions""" - - enable_streaming: bool - """Enable real-time streaming.""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options""" - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py deleted file mode 100644 index 333ff97..0000000 --- a/src/docstrange/types/extract_stream_response.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import TypeAlias - -__all__ = ["ExtractStreamResponse"] - -ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 78813f6..242ce95 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict from .._types import FileTypes @@ -11,31 +11,7 @@ class ExtractSyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" + """File to upload""" output_format: Required[str] - """Output format(s): `markdown`, `html`, `json`, `csv`. - - Comma-separate for multiple (e.g., `markdown,json`). - """ - - csv_options: str - """CSV extraction options (e.g., `table`)""" - - custom_instructions: str - """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" - - json_options: str - """JSON extraction options.""" - - prompt_mode: Literal["append", "replace"] - """`append`: add to base prompt, `replace`: use only custom instructions""" + """Output format""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py deleted file mode 100644 index bfa97a9..0000000 --- a/src/docstrange/types/extraction_format_result.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel -from .extraction_metadata import ExtractionMetadata - -__all__ = ["ExtractionFormatResult"] - - -class ExtractionFormatResult(BaseModel): - content: object - """Extracted content (string or object)""" - - metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py deleted file mode 100644 index 9792ba0..0000000 --- a/src/docstrange/types/extraction_metadata.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["ExtractionMetadata"] - - -class ExtractionMetadata(BaseModel): - bounding_boxes: Optional[object] = None - """Element coordinates with page dimensions""" - - confidence_score: Optional[object] = None - """Confidence scores per field""" diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py deleted file mode 100644 index 57b2928..0000000 --- a/src/docstrange/types/extraction_result.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from pydantic import Field as FieldInfo - -from .._models import BaseModel -from .extraction_format_result import ExtractionFormatResult - -__all__ = ["ExtractionResult"] - - -class ExtractionResult(BaseModel): - """Results by format (only requested formats populated)""" - - csv: Optional[ExtractionFormatResult] = None - - html: Optional[ExtractionFormatResult] = None - - json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) - - markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py deleted file mode 100644 index 397d924..0000000 --- a/src/docstrange/types/file_classification_result.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional - -from .._models import BaseModel -from .page_classification import PageClassification - -__all__ = ["FileClassificationResult"] - - -class FileClassificationResult(BaseModel): - filename: str - - pages: List[PageClassification] - - total_pages: int - - error: Optional[str] = None - - processing_time: Optional[float] = None - """Time in seconds""" - - record_id: Optional[str] = None - """Record ID for polling results""" diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py deleted file mode 100644 index 62a2338..0000000 --- a/src/docstrange/types/page_classification.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["PageClassification"] - - -class PageClassification(BaseModel): - category: str - """Matched category name or 'Other' if no match""" - - confidence: int - """Confidence score (0-100)""" - - page_number: int - - reasoning: str - """Explanation for the classification""" - - identified_category: Optional[str] = None - """Specific document type identified by the model.""" diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py deleted file mode 100644 index ee1d5f5..0000000 --- a/tests/api_resources/extract/test_results.py +++ /dev/null @@ -1,205 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse -from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestResults: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_retrieve(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_retrieve(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_retrieve(self, client: Docstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list(self, client: Docstrange) -> None: - result = client.extract.results.list() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_list(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_list(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncResults: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - await async_client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py deleted file mode 100644 index 84bfced..0000000 --- a/tests/api_resources/test_chat.py +++ /dev/null @@ -1,171 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestChat: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_create_completion(self, client: Docstrange) -> None: - response = client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_create_completion(self, client: Docstrange) -> None: - with client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncChat: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: - response = await async_client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: - async with async_client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index d40e1fa..63539df 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse, BatchClassifyResponse +from docstrange.types import ClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,43 +17,6 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - classify = client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -97,43 +60,6 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index 4f68b18..d8117c2 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,10 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ( - ExtractResponse, - BatchExtractResponse, -) +from docstrange.types import ExtractResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,163 +17,6 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="output_format", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_async(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="output_format", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_async(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="output_format", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="output_format", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="output_format", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream_with_all_params(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - enable_streaming=True, - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_raw_response_stream(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="output_format", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = response.parse() - stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_streaming_response_stream(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = response.parse() - stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -186,22 +26,6 @@ def test_method_sync(self, client: Docstrange) -> None: ) assert_matches_type(ExtractResponse, extract, path=["response"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_sync_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.sync( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: @@ -236,163 +60,6 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="output_format", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="output_format", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="output_format", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="output_format", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="output_format", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - enable_streaming=True, - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="output_format", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = await response.parse() - await stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="output_format", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = await response.parse() - await stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @@ -402,22 +69,6 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: ) assert_matches_type(ExtractResponse, extract, path=["response"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.sync( - file=b"raw file contents", - output_format="output_format", - csv_options="csv_options", - custom_instructions="custom_instructions", - file_base64="file_base64", - file_url="file_url", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: From cee4e51ef55edee2402626eeb04dfe74f41c4676 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:22:41 +0000 Subject: [PATCH 18/26] feat(api): api update --- .stats.yml | 6 +- README.md | 2 +- api.md | 2 +- src/docstrange/_client.py | 2 +- src/docstrange/pagination.py | 67 --------------- src/docstrange/resources/classify.py | 4 +- .../resources/{extract => }/extract.py | 81 ++++++++++++++++--- src/docstrange/resources/extract/__init__.py | 19 ----- src/docstrange/types/classify_sync_params.py | 2 +- src/docstrange/types/extract/__init__.py | 3 - src/docstrange/types/extract_sync_params.py | 27 ++++++- tests/api_resources/extract/__init__.py | 1 - tests/api_resources/test_extract.py | 44 ++++++++-- tests/test_client.py | 24 +++--- 14 files changed, 150 insertions(+), 134 deletions(-) delete mode 100644 src/docstrange/pagination.py rename src/docstrange/resources/{extract => }/extract.py (70%) delete mode 100644 src/docstrange/resources/extract/__init__.py delete mode 100644 src/docstrange/types/extract/__init__.py delete mode 100644 tests/api_resources/extract/__init__.py diff --git a/.stats.yml b/.stats.yml index 1d97927..457a2a7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-3843cc83cc328a1c86126018a4309fbcd13aac4df5fd9afefd652e21e59530ab.yml -openapi_spec_hash: b4046d95714426b15edb8ac003f904c6 -config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-eed2d21f54dac82f72faa7f05408973593e5a6338717864d4f37bf2333274518.yml +openapi_spec_hash: 47cb78aa2caa018fe58742fc19b0a49c +config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 diff --git a/README.md b/README.md index 622faec..74c6d8b 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ client = Docstrange() client.extract.sync( file=Path("/path/to/file"), - output_format="output_format", + output_format="markdown", ) ``` diff --git a/api.md b/api.md index 90358a6..0cac7c9 100644 --- a/api.md +++ b/api.md @@ -8,7 +8,7 @@ from docstrange.types import ExtractResponse Methods: -- client.extract.sync(\*\*params) -> ExtractResponse +- client.extract.sync(\*\*params) -> ExtractResponse # Classify diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 271a014..42d282d 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -32,8 +32,8 @@ if TYPE_CHECKING: from .resources import extract, classify + from .resources.extract import ExtractResource, AsyncExtractResource from .resources.classify import ClassifyResource, AsyncClassifyResource - from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py deleted file mode 100644 index e926095..0000000 --- a/src/docstrange/pagination.py +++ /dev/null @@ -1,67 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Generic, TypeVar, Optional, cast -from typing_extensions import override - -from ._models import BaseModel -from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage - -__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] - -_T = TypeVar("_T") - - -class PageNumberPaginationPagination(BaseModel): - has_next: Optional[bool] = None - - total_count: Optional[int] = None - - -class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) - - -class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index 69045f4..d7853b4 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -59,7 +59,7 @@ def sync( Classify a single document. Args: - categories: JSON array of categories + categories: JSON array of category objects file: File to classify @@ -129,7 +129,7 @@ async def sync( Classify a single document. Args: - categories: JSON array of categories + categories: JSON array of category objects file: File to classify diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract.py similarity index 70% rename from src/docstrange/resources/extract/extract.py rename to src/docstrange/resources/extract.py index 0fe0d14..5b896e5 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract.py @@ -3,22 +3,23 @@ from __future__ import annotations from typing import Mapping, cast +from typing_extensions import Literal import httpx -from ...types import extract_sync_params -from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import extract_sync_params +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.extract_response import ExtractResponse +from .._base_client import make_request_options +from ..types.extract_response import ExtractResponse __all__ = ["ExtractResource", "AsyncExtractResource"] @@ -48,6 +49,13 @@ def sync( *, file: FileTypes, output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -59,9 +67,23 @@ def sync( Process a document and return extracted content immediately. Args: - file: File to upload + file: File to upload (PDF, Word, Excel, PowerPoint, images) - output_format: Output format + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + prompt_mode: append or replace extra_headers: Send extra headers @@ -75,6 +97,13 @@ def sync( { "file": file, "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) @@ -118,6 +147,13 @@ async def sync( *, file: FileTypes, output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -129,9 +165,23 @@ async def sync( Process a document and return extracted content immediately. Args: - file: File to upload + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options - output_format: Output format + prompt_mode: append or replace extra_headers: Send extra headers @@ -145,6 +195,13 @@ async def sync( { "file": file, "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py deleted file mode 100644 index a0fee27..0000000 --- a/src/docstrange/resources/extract/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .extract import ( - ExtractResource, - AsyncExtractResource, - ExtractResourceWithRawResponse, - AsyncExtractResourceWithRawResponse, - ExtractResourceWithStreamingResponse, - AsyncExtractResourceWithStreamingResponse, -) - -__all__ = [ - "ExtractResource", - "AsyncExtractResource", - "ExtractResourceWithRawResponse", - "AsyncExtractResourceWithRawResponse", - "ExtractResourceWithStreamingResponse", - "AsyncExtractResourceWithStreamingResponse", -] diff --git a/src/docstrange/types/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py index ff11627..d0b3e16 100644 --- a/src/docstrange/types/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -11,7 +11,7 @@ class ClassifySyncParams(TypedDict, total=False): categories: Required[str] - """JSON array of categories""" + """JSON array of category objects""" file: Required[FileTypes] """File to classify""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py deleted file mode 100644 index f8ee8b1..0000000 --- a/src/docstrange/types/extract/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 242ce95..1f032ea 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -11,7 +11,28 @@ class ExtractSyncParams(TypedDict, total=False): file: Required[FileTypes] - """File to upload""" + """File to upload (PDF, Word, Excel, PowerPoint, images)""" output_format: Required[str] - """Output format""" + """Output format(s): markdown, html, json, csv.""" + + csv_options: str + """CSV extraction options""" + + custom_instructions: str + """Custom extraction instructions""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata types""" + + json_options: str + """JSON extraction options""" + + prompt_mode: Literal["append", "replace"] + """append or replace""" diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/extract/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index d8117c2..d911970 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -22,7 +22,23 @@ class TestExtract: def test_method_sync(self, client: Docstrange) -> None: extract = client.extract.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_sync_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.sync( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -31,7 +47,7 @@ def test_method_sync(self, client: Docstrange) -> None: def test_raw_response_sync(self, client: Docstrange) -> None: response = client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) assert response.is_closed is True @@ -44,7 +60,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: def test_streaming_response_sync(self, client: Docstrange) -> None: with client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -65,7 +81,23 @@ class TestAsyncExtract: async def test_method_sync(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.sync( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -74,7 +106,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) assert response.is_closed is True @@ -87,7 +119,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.sync( file=b"raw file contents", - output_format="output_format", + output_format="markdown", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index 2756693..719c826 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,9 +862,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" - ).__enter__() + client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @@ -874,9 +872,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" - ).__enter__() + client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -905,7 +901,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") + response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -930,7 +926,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -955,7 +951,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1773,7 +1769,7 @@ async def test_retrying_timeout_errors_doesnt_leak( with pytest.raises(APITimeoutError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" + file=b"raw file contents", output_format="markdown" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1787,7 +1783,7 @@ async def test_retrying_status_errors_doesnt_leak( with pytest.raises(APIStatusError): await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="output_format" + file=b"raw file contents", output_format="markdown" ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1817,7 +1813,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="output_format") + response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1842,7 +1838,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": Omit()} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1867,7 +1863,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="output_format", extra_headers={"x-stainless-retry-count": "42"} + file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 0cb48631aa0c08788f26a3abb051fef2c12aaa20 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:25:29 +0000 Subject: [PATCH 19/26] feat(api): api update --- .stats.yml | 8 +- README.md | 61 ++ api.md | 52 +- src/docstrange/_client.py | 41 +- src/docstrange/pagination.py | 67 ++ src/docstrange/resources/__init__.py | 14 + src/docstrange/resources/chat.py | 182 +++++ src/docstrange/resources/classify.py | 147 +++- src/docstrange/resources/extract.py | 256 ------- src/docstrange/resources/extract/__init__.py | 33 + src/docstrange/resources/extract/extract.py | 724 ++++++++++++++++++ src/docstrange/resources/extract/results.py | 290 +++++++ src/docstrange/types/__init__.py | 13 + .../types/batch_classify_response.py | 24 + .../types/batch_extract_response.py | 24 + .../types/chat_create_completion_params.py | 40 + src/docstrange/types/classify_batch_params.py | 17 + src/docstrange/types/classify_response.py | 5 + src/docstrange/types/extract/__init__.py | 8 + .../types/extract/extraction_list_response.py | 17 + .../types/extract/pagination_info.py | 21 + .../types/extract/result_list_params.py | 17 + .../types/extract/result_retrieve_params.py | 11 + src/docstrange/types/extract_async_params.py | 37 + src/docstrange/types/extract_batch_params.py | 26 + src/docstrange/types/extract_response.py | 21 +- src/docstrange/types/extract_stream_params.py | 35 + .../types/extract_stream_response.py | 7 + src/docstrange/types/extract_sync_params.py | 1 - .../types/extraction_format_result.py | 15 + src/docstrange/types/extraction_metadata.py | 13 + src/docstrange/types/extraction_result.py | 20 + .../types/file_classification_result.py | 22 + src/docstrange/types/page_classification.py | 19 + tests/api_resources/extract/__init__.py | 1 + tests/api_resources/extract/test_results.py | 205 +++++ tests/api_resources/test_chat.py | 171 +++++ tests/api_resources/test_classify.py | 76 +- tests/api_resources/test_extract.py | 319 +++++++- 39 files changed, 2767 insertions(+), 293 deletions(-) create mode 100644 src/docstrange/pagination.py create mode 100644 src/docstrange/resources/chat.py delete mode 100644 src/docstrange/resources/extract.py create mode 100644 src/docstrange/resources/extract/__init__.py create mode 100644 src/docstrange/resources/extract/extract.py create mode 100644 src/docstrange/resources/extract/results.py create mode 100644 src/docstrange/types/batch_classify_response.py create mode 100644 src/docstrange/types/batch_extract_response.py create mode 100644 src/docstrange/types/chat_create_completion_params.py create mode 100644 src/docstrange/types/classify_batch_params.py create mode 100644 src/docstrange/types/extract/__init__.py create mode 100644 src/docstrange/types/extract/extraction_list_response.py create mode 100644 src/docstrange/types/extract/pagination_info.py create mode 100644 src/docstrange/types/extract/result_list_params.py create mode 100644 src/docstrange/types/extract/result_retrieve_params.py create mode 100644 src/docstrange/types/extract_async_params.py create mode 100644 src/docstrange/types/extract_batch_params.py create mode 100644 src/docstrange/types/extract_stream_params.py create mode 100644 src/docstrange/types/extract_stream_response.py create mode 100644 src/docstrange/types/extraction_format_result.py create mode 100644 src/docstrange/types/extraction_metadata.py create mode 100644 src/docstrange/types/extraction_result.py create mode 100644 src/docstrange/types/file_classification_result.py create mode 100644 src/docstrange/types/page_classification.py create mode 100644 tests/api_resources/extract/__init__.py create mode 100644 tests/api_resources/extract/test_results.py create mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 457a2a7..031d39f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-eed2d21f54dac82f72faa7f05408973593e5a6338717864d4f37bf2333274518.yml -openapi_spec_hash: 47cb78aa2caa018fe58742fc19b0a49c -config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 +configured_endpoints: 9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-4dc8746b7c32d8b0ed281e28dc83a6af06e280f87a173d4535a6e7e0db5d1fe6.yml +openapi_spec_hash: c6154bc9b0c0f3611223180cc335673b +config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 diff --git a/README.md b/README.md index 74c6d8b..991dcba 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,67 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Docstrange API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from docstrange import Docstrange + +client = Docstrange() + +all_results = [] +# Automatically fetches more pages as needed. +for result in client.extract.results.list(): + # Do something with result here + all_results.append(result) +print(all_results) +``` + +Or, asynchronously: + +```python +import asyncio +from docstrange import AsyncDocstrange + +client = AsyncDocstrange() + + +async def main() -> None: + all_results = [] + # Iterate through items across all pages, issuing requests as needed. + async for result in client.extract.results.list(): + all_results.append(result) + print(all_results) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.extract.results.list() +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.results)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.extract.results.list() +for result in first_page.results: + print(result.record_id) + +# Remove `await` for non-async usage. +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 0cac7c9..7ccbce8 100644 --- a/api.md +++ b/api.md @@ -3,21 +3,67 @@ Types: ```python -from docstrange.types import ExtractResponse +from docstrange.types import ( + BatchExtractRequestBody, + BatchExtractResponse, + ExtractRequestBody, + ExtractResponse, + ExtractionFormatResult, + ExtractionMetadata, + ExtractionResult, + StreamExtractRequestBody, + ExtractStreamResponse, +) ``` Methods: -- client.extract.sync(\*\*params) -> ExtractResponse +- client.extract.async\_(\*\*params) -> ExtractResponse +- client.extract.batch(\*\*params) -> BatchExtractResponse +- client.extract.stream(\*\*params) -> str +- client.extract.sync(\*\*params) -> ExtractResponse + +## Results + +Types: + +```python +from docstrange.types.extract import ExtractionListResponse, PaginationInfo +``` + +Methods: + +- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse +- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] # Classify Types: ```python -from docstrange.types import ClassifyResponse +from docstrange.types import ( + BatchClassifyRequestBody, + BatchClassifyResponse, + ClassifyRequestBody, + ClassifyResponse, + FileClassificationResult, + PageClassification, +) ``` Methods: +- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse + +# Chat + +Types: + +```python +from docstrange.types import ChatCompletionsRequest +``` + +Methods: + +- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 42d282d..4c5bcc4 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,9 +31,10 @@ ) if TYPE_CHECKING: - from .resources import extract, classify - from .resources.extract import ExtractResource, AsyncExtractResource + from .resources import chat, extract, classify + from .resources.chat import ChatResource, AsyncChatResource from .resources.classify import ClassifyResource, AsyncClassifyResource + from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -114,6 +115,12 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) + @cached_property + def chat(self) -> ChatResource: + from .resources.chat import ChatResource + + return ChatResource(self) + @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -294,6 +301,12 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) + @cached_property + def chat(self) -> AsyncChatResource: + from .resources.chat import AsyncChatResource + + return AsyncChatResource(self) + @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -425,6 +438,12 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithRawResponse: + from .resources.chat import ChatResourceWithRawResponse + + return ChatResourceWithRawResponse(self._client.chat) + class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -444,6 +463,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithRawResponse: + from .resources.chat import AsyncChatResourceWithRawResponse + + return AsyncChatResourceWithRawResponse(self._client.chat) + class DocstrangeWithStreamedResponse: _client: Docstrange @@ -463,6 +488,12 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithStreamingResponse: + from .resources.chat import ChatResourceWithStreamingResponse + + return ChatResourceWithStreamingResponse(self._client.chat) + class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -482,6 +513,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: + from .resources.chat import AsyncChatResourceWithStreamingResponse + + return AsyncChatResourceWithStreamingResponse(self._client.chat) + Client = Docstrange diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py new file mode 100644 index 0000000..e926095 --- /dev/null +++ b/src/docstrange/pagination.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional, cast +from typing_extensions import override + +from ._models import BaseModel +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] + +_T = TypeVar("_T") + + +class PageNumberPaginationPagination(BaseModel): + has_next: Optional[bool] = None + + total_count: Optional[int] = None + + +class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) + + +class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index 6d7ff55..a3172dd 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) from .extract import ( ExtractResource, AsyncExtractResource, @@ -30,4 +38,10 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py new file mode 100644 index 0000000..7e8899d --- /dev/null +++ b/src/docstrange/resources/chat.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..types import chat_create_completion_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + OpenAI-compatible chat completions endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + OpenAI-compatible chat completions endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_raw_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_raw_response_wrapper( + chat.create_completion, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_streamed_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_streamed_response_wrapper( + chat.create_completion, + ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index d7853b4..ded77af 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,13 +2,11 @@ from __future__ import annotations -from typing import Mapping, cast - import httpx -from ..types import classify_sync_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..types import classify_sync_params, classify_batch_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -19,6 +17,7 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse +from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -43,6 +42,53 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) + def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50). + + Args: + categories: JSON array of category objects + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/classify/batch", + body=maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + def sync( self, *, @@ -71,21 +117,19 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -113,6 +157,53 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) + async def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50). + + Args: + categories: JSON array of category objects + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/classify/batch", + body=await async_maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + async def sync( self, *, @@ -141,21 +232,19 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=await async_maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -167,6 +256,9 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_raw_response_wrapper( + classify.batch, + ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -176,6 +268,9 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_raw_response_wrapper( + classify.batch, + ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -185,6 +280,9 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_streamed_response_wrapper( + classify.batch, + ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -194,6 +292,9 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_streamed_response_wrapper( + classify.batch, + ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract.py b/src/docstrange/resources/extract.py deleted file mode 100644 index 5b896e5..0000000 --- a/src/docstrange/resources/extract.py +++ /dev/null @@ -1,256 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Mapping, cast -from typing_extensions import Literal - -import httpx - -from ..types import extract_sync_params -from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.extract_response import ExtractResponse - -__all__ = ["ExtractResource", "AsyncExtractResource"] - - -class ExtractResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ExtractResourceWithStreamingResponse(self) - - def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - prompt_mode: append or replace - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/sync", - body=maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class AsyncExtractResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncExtractResourceWithStreamingResponse(self) - - async def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - prompt_mode: append or replace - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class ExtractResourceWithRawResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_raw_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithRawResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_raw_response_wrapper( - extract.sync, - ) - - -class ExtractResourceWithStreamingResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_streamed_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithStreamingResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_streamed_response_wrapper( - extract.sync, - ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py new file mode 100644 index 0000000..22cd257 --- /dev/null +++ b/src/docstrange/resources/extract/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .extract import ( + ExtractResource, + AsyncExtractResource, + ExtractResourceWithRawResponse, + AsyncExtractResourceWithRawResponse, + ExtractResourceWithStreamingResponse, + AsyncExtractResourceWithStreamingResponse, +) +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) + +__all__ = [ + "ResultsResource", + "AsyncResultsResource", + "ResultsResourceWithRawResponse", + "AsyncResultsResourceWithRawResponse", + "ResultsResourceWithStreamingResponse", + "AsyncResultsResourceWithStreamingResponse", + "ExtractResource", + "AsyncExtractResource", + "ExtractResourceWithRawResponse", + "AsyncExtractResourceWithRawResponse", + "ExtractResourceWithStreamingResponse", + "AsyncExtractResourceWithStreamingResponse", +] diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py new file mode 100644 index 0000000..335f013 --- /dev/null +++ b/src/docstrange/resources/extract/extract.py @@ -0,0 +1,724 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.extract_response import ExtractResponse +from ...types.batch_extract_response import BatchExtractResponse +from ...types.extract_stream_response import ExtractStreamResponse + +__all__ = ["ExtractResource", "AsyncExtractResource"] + + +class ExtractResource(SyncAPIResource): + @cached_property + def results(self) -> ResultsResource: + return ResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> ExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ExtractResourceWithStreamingResponse(self) + + def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Queue a document for background processing. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/async", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """ + Process multiple files asynchronously (max 50). + + Args: + files: Files to process (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/batch", + body=maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ExtractStreamResponse]: + """ + Stream extraction results via SSE. + + Args: + file: File to upload + + output_format: Output format(s) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/stream", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=Stream[ExtractStreamResponse], + ) + + def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class AsyncExtractResource(AsyncAPIResource): + @cached_property + def results(self) -> AsyncResultsResource: + return AsyncResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncExtractResourceWithStreamingResponse(self) + + async def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Queue a document for background processing. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/async", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + async def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """ + Process multiple files asynchronously (max 50). + + Args: + files: Files to process (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/batch", + body=await async_maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + async def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ExtractStreamResponse]: + """ + Stream extraction results via SSE. + + Args: + file: File to upload + + output_format: Output format(s) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/stream", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=AsyncStream[ExtractStreamResponse], + ) + + async def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class ExtractResourceWithRawResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_raw_response_wrapper( + extract.async_, + ) + self.batch = to_raw_response_wrapper( + extract.batch, + ) + self.stream = to_raw_response_wrapper( + extract.stream, + ) + self.sync = to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithRawResponse: + return ResultsResourceWithRawResponse(self._extract.results) + + +class AsyncExtractResourceWithRawResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_raw_response_wrapper( + extract.async_, + ) + self.batch = async_to_raw_response_wrapper( + extract.batch, + ) + self.stream = async_to_raw_response_wrapper( + extract.stream, + ) + self.sync = async_to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithRawResponse: + return AsyncResultsResourceWithRawResponse(self._extract.results) + + +class ExtractResourceWithStreamingResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_streamed_response_wrapper( + extract.async_, + ) + self.batch = to_streamed_response_wrapper( + extract.batch, + ) + self.stream = to_streamed_response_wrapper( + extract.stream, + ) + self.sync = to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithStreamingResponse: + return ResultsResourceWithStreamingResponse(self._extract.results) + + +class AsyncExtractResourceWithStreamingResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_streamed_response_wrapper( + extract.async_, + ) + self.batch = async_to_streamed_response_wrapper( + extract.batch, + ) + self.stream = async_to_streamed_response_wrapper( + extract.stream, + ) + self.sync = async_to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithStreamingResponse: + return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py new file mode 100644 index 0000000..bf20529 --- /dev/null +++ b/src/docstrange/resources/extract/results.py @@ -0,0 +1,290 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination +from ..._base_client import AsyncPaginator, make_request_options +from ...types.extract import result_list_params, result_retrieve_params +from ...types.extract_response import ExtractResponse + +__all__ = ["ResultsResource", "AsyncResultsResource"] + + +class ResultsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ResultsResourceWithStreamingResponse(self) + + def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPageNumberPagination[ExtractResponse]: + """ + List all extraction jobs (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=SyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class AsyncResultsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncResultsResourceWithStreamingResponse(self) + + async def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return await self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: + """ + List all extraction jobs (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=AsyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class ResultsResourceWithRawResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_raw_response_wrapper( + results.retrieve, + ) + self.list = to_raw_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithRawResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_raw_response_wrapper( + results.retrieve, + ) + self.list = async_to_raw_response_wrapper( + results.list, + ) + + +class ResultsResourceWithStreamingResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_streamed_response_wrapper( + results.retrieve, + ) + self.list = to_streamed_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithStreamingResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_streamed_response_wrapper( + results.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + results.list, + ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index fac3955..a18a10a 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,5 +4,18 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse +from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams +from .extraction_metadata import ExtractionMetadata as ExtractionMetadata +from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams +from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams +from .extract_batch_params import ExtractBatchParams as ExtractBatchParams +from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams +from .extract_stream_params import ExtractStreamParams as ExtractStreamParams +from .batch_extract_response import BatchExtractResponse as BatchExtractResponse +from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse +from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse +from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult +from .file_classification_result import FileClassificationResult as FileClassificationResult +from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py new file mode 100644 index 0000000..a36e5a8 --- /dev/null +++ b/src/docstrange/types/batch_classify_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .file_classification_result import FileClassificationResult + +__all__ = ["BatchClassifyResponse"] + + +class BatchClassifyResponse(BaseModel): + batch_id: str + + failed_files: int + + message: str + + results: List[FileClassificationResult] + + success: bool + + successful_files: int + + total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py new file mode 100644 index 0000000..4e73773 --- /dev/null +++ b/src/docstrange/types/batch_extract_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .extract_response import ExtractResponse + +__all__ = ["BatchExtractResponse"] + + +class BatchExtractResponse(BaseModel): + accepted_files: int + + batch_id: str + + message: str + + records: List[ExtractResponse] + + rejected_files: int + + success: bool + + total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py new file mode 100644 index 0000000..eea9be6 --- /dev/null +++ b/src/docstrange/types/chat_create_completion_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] + + +class ChatCreateCompletionParams(TypedDict, total=False): + messages: Required[Iterable[Message]] + + model: Required[str] + + stream: bool + + +class MessageContentFileURL(TypedDict, total=False): + url: str + + +class MessageContentImageURL(TypedDict, total=False): + url: str + + +class MessageContent(TypedDict, total=False): + file_url: MessageContentFileURL + + image_url: MessageContentImageURL + + text: str + + type: str + + +class Message(TypedDict, total=False): + content: Required[Iterable[MessageContent]] + + role: Required[str] diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py new file mode 100644 index 0000000..1f23cd0 --- /dev/null +++ b/src/docstrange/types/classify_batch_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ClassifyBatchParams"] + + +class ClassifyBatchParams(TypedDict, total=False): + categories: Required[str] + """JSON array of category objects""" + + files: Required[SequenceNotStr[FileTypes]] + """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index 5bab3de..fdebbb0 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,6 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from .._models import BaseModel +from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -9,3 +12,5 @@ class ClassifyResponse(BaseModel): message: str success: bool + + result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py new file mode 100644 index 0000000..387a007 --- /dev/null +++ b/src/docstrange/types/extract/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .pagination_info import PaginationInfo as PaginationInfo +from .result_list_params import ResultListParams as ResultListParams +from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams +from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py new file mode 100644 index 0000000..26a12e7 --- /dev/null +++ b/src/docstrange/types/extract/extraction_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .pagination_info import PaginationInfo +from ..extract_response import ExtractResponse + +__all__ = ["ExtractionListResponse"] + + +class ExtractionListResponse(BaseModel): + pagination: PaginationInfo + + results: List[ExtractResponse] + + success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py new file mode 100644 index 0000000..7a678a9 --- /dev/null +++ b/src/docstrange/types/extract/pagination_info.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["PaginationInfo"] + + +class PaginationInfo(BaseModel): + has_next: Optional[bool] = None + + has_previous: Optional[bool] = None + + page: Optional[int] = None + + page_size: Optional[int] = None + + total_count: Optional[int] = None + + total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py new file mode 100644 index 0000000..7dfe38a --- /dev/null +++ b/src/docstrange/types/extract/result_list_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ResultListParams"] + + +class ResultListParams(TypedDict, total=False): + page: int + + page_size: int + + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + + sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py new file mode 100644 index 0000000..1de1a80 --- /dev/null +++ b/src/docstrange/types/extract/result_retrieve_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ResultRetrieveParams"] + + +class ResultRetrieveParams(TypedDict, total=False): + include_content: bool diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py new file mode 100644 index 0000000..2895923 --- /dev/null +++ b/src/docstrange/types/extract_async_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractAsyncParams"] + + +class ExtractAsyncParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + output_format: Required[str] + """Output format(s): markdown, html, json, csv.""" + + csv_options: str + """CSV extraction options""" + + custom_instructions: str + """Custom extraction instructions""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata types""" + + json_options: str + """JSON extraction options""" + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py new file mode 100644 index 0000000..380d386 --- /dev/null +++ b/src/docstrange/types/extract_batch_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ExtractBatchParams"] + + +class ExtractBatchParams(TypedDict, total=False): + files: Required[SequenceNotStr[FileTypes]] + """Files to process (max 50)""" + + output_format: Required[str] + + csv_options: str + + custom_instructions: str + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index ba021e0..a4d3ed3 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,17 +1,34 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime +from typing_extensions import Literal from .._models import BaseModel +from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): + message: str + record_id: str - status: str + status: Literal["completed", "processing", "failed"] success: bool - message: Optional[str] = None + created_at: Optional[datetime] = None + + file_size: Optional[int] = None + + filename: Optional[str] = None + + output_format: Optional[str] = None + + pages_processed: Optional[int] = None + + processing_time: Optional[float] = None + + result: Optional[ExtractionResult] = None diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py new file mode 100644 index 0000000..52147de --- /dev/null +++ b/src/docstrange/types/extract_stream_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractStreamParams"] + + +class ExtractStreamParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload""" + + output_format: Required[str] + """Output format(s)""" + + csv_options: str + + custom_instructions: str + + enable_streaming: bool + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py new file mode 100644 index 0000000..333ff97 --- /dev/null +++ b/src/docstrange/types/extract_stream_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ExtractStreamResponse"] + +ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 1f032ea..3208694 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -35,4 +35,3 @@ class ExtractSyncParams(TypedDict, total=False): """JSON extraction options""" prompt_mode: Literal["append", "replace"] - """append or replace""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py new file mode 100644 index 0000000..579500c --- /dev/null +++ b/src/docstrange/types/extraction_format_result.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional + +from .._models import BaseModel +from .extraction_metadata import ExtractionMetadata + +__all__ = ["ExtractionFormatResult"] + + +class ExtractionFormatResult(BaseModel): + content: Union[str, object] + """Extracted content""" + + metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py new file mode 100644 index 0000000..d206781 --- /dev/null +++ b/src/docstrange/types/extraction_metadata.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExtractionMetadata"] + + +class ExtractionMetadata(BaseModel): + bounding_boxes: Optional[object] = None + + confidence_score: Optional[object] = None diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py new file mode 100644 index 0000000..8cd1772 --- /dev/null +++ b/src/docstrange/types/extraction_result.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .extraction_format_result import ExtractionFormatResult + +__all__ = ["ExtractionResult"] + + +class ExtractionResult(BaseModel): + csv: Optional[ExtractionFormatResult] = None + + html: Optional[ExtractionFormatResult] = None + + json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) + + markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py new file mode 100644 index 0000000..3c1b290 --- /dev/null +++ b/src/docstrange/types/file_classification_result.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .page_classification import PageClassification + +__all__ = ["FileClassificationResult"] + + +class FileClassificationResult(BaseModel): + filename: str + + pages: List[PageClassification] + + total_pages: int + + error: Optional[str] = None + + processing_time: Optional[float] = None + + record_id: Optional[str] = None diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py new file mode 100644 index 0000000..afc73a3 --- /dev/null +++ b/src/docstrange/types/page_classification.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["PageClassification"] + + +class PageClassification(BaseModel): + category: str + + confidence: int + + page_number: int + + reasoning: str + + identified_category: Optional[str] = None diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/extract/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py new file mode 100644 index 0000000..ee1d5f5 --- /dev/null +++ b/tests/api_resources/extract/test_results.py @@ -0,0 +1,205 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type +from docstrange.types import ExtractResponse +from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResults: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Docstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Docstrange) -> None: + result = client.extract.results.list() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncResults: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + await async_client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py new file mode 100644 index 0000000..84bfced --- /dev/null +++ b/tests/api_resources/test_chat.py @@ -0,0 +1,171 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestChat: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "type", + } + ], + "role": "role", + } + ], + model="model", + stream=True, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_completion(self, client: Docstrange) -> None: + response = client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_completion(self, client: Docstrange) -> None: + with client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncChat: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "type", + } + ], + "role": "role", + } + ], + model="model", + stream=True, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: + response = await async_client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: + async with async_client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index 63539df..d40e1fa 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse +from docstrange.types import ClassifyResponse, BatchClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +17,43 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + classify = client.classify.batch( + categories="categories", + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.classify.with_raw_response.batch( + categories="categories", + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.classify.with_streaming_response.batch( + categories="categories", + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -60,6 +97,43 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + classify = await async_client.classify.batch( + categories="categories", + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.classify.with_raw_response.batch( + categories="categories", + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.classify.with_streaming_response.batch( + categories="categories", + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index d911970..bd13f30 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,7 +9,10 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse +from docstrange.types import ( + ExtractResponse, + BatchExtractResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +20,163 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_async(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_async(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream_with_all_params(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_raw_response_stream(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_streaming_response_stream(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -76,6 +236,163 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: From 55c65e11f1cbfcd60df5ec2d65c503330bc29a3c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:27:10 +0000 Subject: [PATCH 20/26] feat(api): api update --- .stats.yml | 8 +- README.md | 61 -- api.md | 52 +- src/docstrange/_client.py | 41 +- src/docstrange/pagination.py | 67 -- src/docstrange/resources/__init__.py | 14 - src/docstrange/resources/chat.py | 182 ----- src/docstrange/resources/classify.py | 147 +--- src/docstrange/resources/extract.py | 256 +++++++ src/docstrange/resources/extract/__init__.py | 33 - src/docstrange/resources/extract/extract.py | 724 ------------------ src/docstrange/resources/extract/results.py | 290 ------- src/docstrange/types/__init__.py | 13 - .../types/batch_classify_response.py | 24 - .../types/batch_extract_response.py | 24 - .../types/chat_create_completion_params.py | 40 - src/docstrange/types/classify_batch_params.py | 17 - src/docstrange/types/classify_response.py | 5 - src/docstrange/types/extract/__init__.py | 8 - .../types/extract/extraction_list_response.py | 17 - .../types/extract/pagination_info.py | 21 - .../types/extract/result_list_params.py | 17 - .../types/extract/result_retrieve_params.py | 11 - src/docstrange/types/extract_async_params.py | 37 - src/docstrange/types/extract_batch_params.py | 26 - src/docstrange/types/extract_response.py | 21 +- src/docstrange/types/extract_stream_params.py | 35 - .../types/extract_stream_response.py | 7 - src/docstrange/types/extract_sync_params.py | 1 + .../types/extraction_format_result.py | 15 - src/docstrange/types/extraction_metadata.py | 13 - src/docstrange/types/extraction_result.py | 20 - .../types/file_classification_result.py | 22 - src/docstrange/types/page_classification.py | 19 - tests/api_resources/extract/__init__.py | 1 - tests/api_resources/extract/test_results.py | 205 ----- tests/api_resources/test_chat.py | 171 ----- tests/api_resources/test_classify.py | 76 +- tests/api_resources/test_extract.py | 319 +------- 39 files changed, 293 insertions(+), 2767 deletions(-) delete mode 100644 src/docstrange/pagination.py delete mode 100644 src/docstrange/resources/chat.py create mode 100644 src/docstrange/resources/extract.py delete mode 100644 src/docstrange/resources/extract/__init__.py delete mode 100644 src/docstrange/resources/extract/extract.py delete mode 100644 src/docstrange/resources/extract/results.py delete mode 100644 src/docstrange/types/batch_classify_response.py delete mode 100644 src/docstrange/types/batch_extract_response.py delete mode 100644 src/docstrange/types/chat_create_completion_params.py delete mode 100644 src/docstrange/types/classify_batch_params.py delete mode 100644 src/docstrange/types/extract/__init__.py delete mode 100644 src/docstrange/types/extract/extraction_list_response.py delete mode 100644 src/docstrange/types/extract/pagination_info.py delete mode 100644 src/docstrange/types/extract/result_list_params.py delete mode 100644 src/docstrange/types/extract/result_retrieve_params.py delete mode 100644 src/docstrange/types/extract_async_params.py delete mode 100644 src/docstrange/types/extract_batch_params.py delete mode 100644 src/docstrange/types/extract_stream_params.py delete mode 100644 src/docstrange/types/extract_stream_response.py delete mode 100644 src/docstrange/types/extraction_format_result.py delete mode 100644 src/docstrange/types/extraction_metadata.py delete mode 100644 src/docstrange/types/extraction_result.py delete mode 100644 src/docstrange/types/file_classification_result.py delete mode 100644 src/docstrange/types/page_classification.py delete mode 100644 tests/api_resources/extract/__init__.py delete mode 100644 tests/api_resources/extract/test_results.py delete mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 031d39f..e24fff1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-4dc8746b7c32d8b0ed281e28dc83a6af06e280f87a173d4535a6e7e0db5d1fe6.yml -openapi_spec_hash: c6154bc9b0c0f3611223180cc335673b -config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 +configured_endpoints: 2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-4a17ecf235fd346afe631381db80e46b26346368a323939de7b0741a5b60ce3d.yml +openapi_spec_hash: c46bca368a4f85e1a684b309e4129ef5 +config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 diff --git a/README.md b/README.md index 991dcba..74c6d8b 100644 --- a/README.md +++ b/README.md @@ -115,67 +115,6 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## Pagination - -List methods in the Docstrange API are paginated. - -This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: - -```python -from docstrange import Docstrange - -client = Docstrange() - -all_results = [] -# Automatically fetches more pages as needed. -for result in client.extract.results.list(): - # Do something with result here - all_results.append(result) -print(all_results) -``` - -Or, asynchronously: - -```python -import asyncio -from docstrange import AsyncDocstrange - -client = AsyncDocstrange() - - -async def main() -> None: - all_results = [] - # Iterate through items across all pages, issuing requests as needed. - async for result in client.extract.results.list(): - all_results.append(result) - print(all_results) - - -asyncio.run(main()) -``` - -Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: - -```python -first_page = await client.extract.results.list() -if first_page.has_next_page(): - print(f"will fetch next page using these details: {first_page.next_page_info()}") - next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.results)}") - -# Remove `await` for non-async usage. -``` - -Or just work directly with the returned data: - -```python -first_page = await client.extract.results.list() -for result in first_page.results: - print(result.record_id) - -# Remove `await` for non-async usage. -``` - ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 7ccbce8..0cac7c9 100644 --- a/api.md +++ b/api.md @@ -3,67 +3,21 @@ Types: ```python -from docstrange.types import ( - BatchExtractRequestBody, - BatchExtractResponse, - ExtractRequestBody, - ExtractResponse, - ExtractionFormatResult, - ExtractionMetadata, - ExtractionResult, - StreamExtractRequestBody, - ExtractStreamResponse, -) +from docstrange.types import ExtractResponse ``` Methods: -- client.extract.async\_(\*\*params) -> ExtractResponse -- client.extract.batch(\*\*params) -> BatchExtractResponse -- client.extract.stream(\*\*params) -> str -- client.extract.sync(\*\*params) -> ExtractResponse - -## Results - -Types: - -```python -from docstrange.types.extract import ExtractionListResponse, PaginationInfo -``` - -Methods: - -- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] +- client.extract.sync(\*\*params) -> ExtractResponse # Classify Types: ```python -from docstrange.types import ( - BatchClassifyRequestBody, - BatchClassifyResponse, - ClassifyRequestBody, - ClassifyResponse, - FileClassificationResult, - PageClassification, -) +from docstrange.types import ClassifyResponse ``` Methods: -- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse - -# Chat - -Types: - -```python -from docstrange.types import ChatCompletionsRequest -``` - -Methods: - -- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 4c5bcc4..42d282d 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,10 +31,9 @@ ) if TYPE_CHECKING: - from .resources import chat, extract, classify - from .resources.chat import ChatResource, AsyncChatResource + from .resources import extract, classify + from .resources.extract import ExtractResource, AsyncExtractResource from .resources.classify import ClassifyResource, AsyncClassifyResource - from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -115,12 +114,6 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) - @cached_property - def chat(self) -> ChatResource: - from .resources.chat import ChatResource - - return ChatResource(self) - @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -301,12 +294,6 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) - @cached_property - def chat(self) -> AsyncChatResource: - from .resources.chat import AsyncChatResource - - return AsyncChatResource(self) - @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -438,12 +425,6 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithRawResponse: - from .resources.chat import ChatResourceWithRawResponse - - return ChatResourceWithRawResponse(self._client.chat) - class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -463,12 +444,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithRawResponse: - from .resources.chat import AsyncChatResourceWithRawResponse - - return AsyncChatResourceWithRawResponse(self._client.chat) - class DocstrangeWithStreamedResponse: _client: Docstrange @@ -488,12 +463,6 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithStreamingResponse: - from .resources.chat import ChatResourceWithStreamingResponse - - return ChatResourceWithStreamingResponse(self._client.chat) - class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -513,12 +482,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: - from .resources.chat import AsyncChatResourceWithStreamingResponse - - return AsyncChatResourceWithStreamingResponse(self._client.chat) - Client = Docstrange diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py deleted file mode 100644 index e926095..0000000 --- a/src/docstrange/pagination.py +++ /dev/null @@ -1,67 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Generic, TypeVar, Optional, cast -from typing_extensions import override - -from ._models import BaseModel -from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage - -__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] - -_T = TypeVar("_T") - - -class PageNumberPaginationPagination(BaseModel): - has_next: Optional[bool] = None - - total_count: Optional[int] = None - - -class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) - - -class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - results: List[_T] - pagination: Optional[PageNumberPaginationPagination] = None - - @override - def _get_page_items(self) -> List[_T]: - results = self.results - if not results: - return [] - return results - - @override - def next_page_info(self) -> Optional[PageInfo]: - last_page = cast("int | None", self._options.params.get("page")) or 1 - - total_pages = None - if self.pagination is not None: - if self.pagination.total_count is not None: - total_pages = self.pagination.total_count - if total_pages is not None and last_page >= total_pages: - return None - - return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index a3172dd..6d7ff55 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) from .extract import ( ExtractResource, AsyncExtractResource, @@ -38,10 +30,4 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py deleted file mode 100644 index 7e8899d..0000000 --- a/src/docstrange/resources/chat.py +++ /dev/null @@ -1,182 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable - -import httpx - -from ..types import chat_create_completion_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/chat/completions", - body=maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/chat/completions", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_raw_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_raw_response_wrapper( - chat.create_completion, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_streamed_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_streamed_response_wrapper( - chat.create_completion, - ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index ded77af..d7853b4 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,11 +2,13 @@ from __future__ import annotations +from typing import Mapping, cast + import httpx -from ..types import classify_sync_params, classify_batch_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given -from .._utils import maybe_transform, async_maybe_transform +from ..types import classify_sync_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,7 +19,6 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse -from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -42,53 +43,6 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) - def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50). - - Args: - categories: JSON array of category objects - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/classify/batch", - body=maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - def sync( self, *, @@ -117,19 +71,21 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -157,53 +113,6 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) - async def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50). - - Args: - categories: JSON array of category objects - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/classify/batch", - body=await async_maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - async def sync( self, *, @@ -232,19 +141,21 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -256,9 +167,6 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_raw_response_wrapper( - classify.batch, - ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -268,9 +176,6 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_raw_response_wrapper( - classify.batch, - ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -280,9 +185,6 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_streamed_response_wrapper( - classify.batch, - ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -292,9 +194,6 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_streamed_response_wrapper( - classify.batch, - ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract.py b/src/docstrange/resources/extract.py new file mode 100644 index 0000000..5b896e5 --- /dev/null +++ b/src/docstrange/resources/extract.py @@ -0,0 +1,256 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast +from typing_extensions import Literal + +import httpx + +from ..types import extract_sync_params +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.extract_response import ExtractResponse + +__all__ = ["ExtractResource", "AsyncExtractResource"] + + +class ExtractResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ExtractResourceWithStreamingResponse(self) + + def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + prompt_mode: append or replace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class AsyncExtractResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncExtractResourceWithStreamingResponse(self) + + async def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + prompt_mode: append or replace + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class ExtractResourceWithRawResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.sync = to_raw_response_wrapper( + extract.sync, + ) + + +class AsyncExtractResourceWithRawResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.sync = async_to_raw_response_wrapper( + extract.sync, + ) + + +class ExtractResourceWithStreamingResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.sync = to_streamed_response_wrapper( + extract.sync, + ) + + +class AsyncExtractResourceWithStreamingResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.sync = async_to_streamed_response_wrapper( + extract.sync, + ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py deleted file mode 100644 index 22cd257..0000000 --- a/src/docstrange/resources/extract/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .extract import ( - ExtractResource, - AsyncExtractResource, - ExtractResourceWithRawResponse, - AsyncExtractResourceWithRawResponse, - ExtractResourceWithStreamingResponse, - AsyncExtractResourceWithStreamingResponse, -) -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) - -__all__ = [ - "ResultsResource", - "AsyncResultsResource", - "ResultsResourceWithRawResponse", - "AsyncResultsResourceWithRawResponse", - "ResultsResourceWithStreamingResponse", - "AsyncResultsResourceWithStreamingResponse", - "ExtractResource", - "AsyncExtractResource", - "ExtractResourceWithRawResponse", - "AsyncExtractResourceWithRawResponse", - "ExtractResourceWithStreamingResponse", - "AsyncExtractResourceWithStreamingResponse", -] diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py deleted file mode 100644 index 335f013..0000000 --- a/src/docstrange/resources/extract/extract.py +++ /dev/null @@ -1,724 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) -from ..._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._streaming import Stream, AsyncStream -from ..._base_client import make_request_options -from ...types.extract_response import ExtractResponse -from ...types.batch_extract_response import BatchExtractResponse -from ...types.extract_stream_response import ExtractStreamResponse - -__all__ = ["ExtractResource", "AsyncExtractResource"] - - -class ExtractResource(SyncAPIResource): - @cached_property - def results(self) -> ResultsResource: - return ResultsResource(self._client) - - @cached_property - def with_raw_response(self) -> ExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ExtractResourceWithStreamingResponse(self) - - def async_( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Queue a document for background processing. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/async", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """ - Process multiple files asynchronously (max 50). - - Args: - files: Files to process (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/batch", - body=maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[ExtractStreamResponse]: - """ - Stream extraction results via SSE. - - Args: - file: File to upload - - output_format: Output format(s) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/stream", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=Stream[ExtractStreamResponse], - ) - - def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/sync", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class AsyncExtractResource(AsyncAPIResource): - @cached_property - def results(self) -> AsyncResultsResource: - return AsyncResultsResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncExtractResourceWithStreamingResponse(self) - - async def async_( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Queue a document for background processing. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/async", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - async def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """ - Process multiple files asynchronously (max 50). - - Args: - files: Files to process (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/batch", - body=await async_maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - async def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[ExtractStreamResponse]: - """ - Stream extraction results via SSE. - - Args: - file: File to upload - - output_format: Output format(s) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/stream", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=AsyncStream[ExtractStreamResponse], - ) - - async def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class ExtractResourceWithRawResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.async_ = to_raw_response_wrapper( - extract.async_, - ) - self.batch = to_raw_response_wrapper( - extract.batch, - ) - self.stream = to_raw_response_wrapper( - extract.stream, - ) - self.sync = to_raw_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> ResultsResourceWithRawResponse: - return ResultsResourceWithRawResponse(self._extract.results) - - -class AsyncExtractResourceWithRawResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.async_ = async_to_raw_response_wrapper( - extract.async_, - ) - self.batch = async_to_raw_response_wrapper( - extract.batch, - ) - self.stream = async_to_raw_response_wrapper( - extract.stream, - ) - self.sync = async_to_raw_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> AsyncResultsResourceWithRawResponse: - return AsyncResultsResourceWithRawResponse(self._extract.results) - - -class ExtractResourceWithStreamingResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.async_ = to_streamed_response_wrapper( - extract.async_, - ) - self.batch = to_streamed_response_wrapper( - extract.batch, - ) - self.stream = to_streamed_response_wrapper( - extract.stream, - ) - self.sync = to_streamed_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> ResultsResourceWithStreamingResponse: - return ResultsResourceWithStreamingResponse(self._extract.results) - - -class AsyncExtractResourceWithStreamingResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.async_ = async_to_streamed_response_wrapper( - extract.async_, - ) - self.batch = async_to_streamed_response_wrapper( - extract.batch, - ) - self.stream = async_to_streamed_response_wrapper( - extract.stream, - ) - self.sync = async_to_streamed_response_wrapper( - extract.sync, - ) - - @cached_property - def results(self) -> AsyncResultsResourceWithStreamingResponse: - return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py deleted file mode 100644 index bf20529..0000000 --- a/src/docstrange/resources/extract/results.py +++ /dev/null @@ -1,290 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination -from ..._base_client import AsyncPaginator, make_request_options -from ...types.extract import result_list_params, result_retrieve_params -from ...types.extract_response import ExtractResponse - -__all__ = ["ResultsResource", "AsyncResultsResource"] - - -class ResultsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ResultsResourceWithStreamingResponse(self) - - def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageNumberPagination[ExtractResponse]: - """ - List all extraction jobs (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=SyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class AsyncResultsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncResultsResourceWithStreamingResponse(self) - - async def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return await self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: - """ - List all extraction jobs (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=AsyncPageNumberPagination[ExtractResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=ExtractResponse, - ) - - -class ResultsResourceWithRawResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_raw_response_wrapper( - results.retrieve, - ) - self.list = to_raw_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithRawResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_raw_response_wrapper( - results.retrieve, - ) - self.list = async_to_raw_response_wrapper( - results.list, - ) - - -class ResultsResourceWithStreamingResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_streamed_response_wrapper( - results.retrieve, - ) - self.list = to_streamed_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithStreamingResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_streamed_response_wrapper( - results.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - results.list, - ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index a18a10a..fac3955 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,18 +4,5 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse -from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams -from .extraction_metadata import ExtractionMetadata as ExtractionMetadata -from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams -from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams -from .extract_batch_params import ExtractBatchParams as ExtractBatchParams -from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams -from .extract_stream_params import ExtractStreamParams as ExtractStreamParams -from .batch_extract_response import BatchExtractResponse as BatchExtractResponse -from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse -from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse -from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult -from .file_classification_result import FileClassificationResult as FileClassificationResult -from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py deleted file mode 100644 index a36e5a8..0000000 --- a/src/docstrange/types/batch_classify_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .file_classification_result import FileClassificationResult - -__all__ = ["BatchClassifyResponse"] - - -class BatchClassifyResponse(BaseModel): - batch_id: str - - failed_files: int - - message: str - - results: List[FileClassificationResult] - - success: bool - - successful_files: int - - total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py deleted file mode 100644 index 4e73773..0000000 --- a/src/docstrange/types/batch_extract_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel -from .extract_response import ExtractResponse - -__all__ = ["BatchExtractResponse"] - - -class BatchExtractResponse(BaseModel): - accepted_files: int - - batch_id: str - - message: str - - records: List[ExtractResponse] - - rejected_files: int - - success: bool - - total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py deleted file mode 100644 index eea9be6..0000000 --- a/src/docstrange/types/chat_create_completion_params.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, TypedDict - -__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] - - -class ChatCreateCompletionParams(TypedDict, total=False): - messages: Required[Iterable[Message]] - - model: Required[str] - - stream: bool - - -class MessageContentFileURL(TypedDict, total=False): - url: str - - -class MessageContentImageURL(TypedDict, total=False): - url: str - - -class MessageContent(TypedDict, total=False): - file_url: MessageContentFileURL - - image_url: MessageContentImageURL - - text: str - - type: str - - -class Message(TypedDict, total=False): - content: Required[Iterable[MessageContent]] - - role: Required[str] diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py deleted file mode 100644 index 1f23cd0..0000000 --- a/src/docstrange/types/classify_batch_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ClassifyBatchParams"] - - -class ClassifyBatchParams(TypedDict, total=False): - categories: Required[str] - """JSON array of category objects""" - - files: Required[SequenceNotStr[FileTypes]] - """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index fdebbb0..5bab3de 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,9 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional - from .._models import BaseModel -from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -12,5 +9,3 @@ class ClassifyResponse(BaseModel): message: str success: bool - - result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py deleted file mode 100644 index 387a007..0000000 --- a/src/docstrange/types/extract/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .pagination_info import PaginationInfo as PaginationInfo -from .result_list_params import ResultListParams as ResultListParams -from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams -from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py deleted file mode 100644 index 26a12e7..0000000 --- a/src/docstrange/types/extract/extraction_list_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel -from .pagination_info import PaginationInfo -from ..extract_response import ExtractResponse - -__all__ = ["ExtractionListResponse"] - - -class ExtractionListResponse(BaseModel): - pagination: PaginationInfo - - results: List[ExtractResponse] - - success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py deleted file mode 100644 index 7a678a9..0000000 --- a/src/docstrange/types/extract/pagination_info.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["PaginationInfo"] - - -class PaginationInfo(BaseModel): - has_next: Optional[bool] = None - - has_previous: Optional[bool] = None - - page: Optional[int] = None - - page_size: Optional[int] = None - - total_count: Optional[int] = None - - total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py deleted file mode 100644 index 7dfe38a..0000000 --- a/src/docstrange/types/extract/result_list_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, TypedDict - -__all__ = ["ResultListParams"] - - -class ResultListParams(TypedDict, total=False): - page: int - - page_size: int - - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - - sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py deleted file mode 100644 index 1de1a80..0000000 --- a/src/docstrange/types/extract/result_retrieve_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["ResultRetrieveParams"] - - -class ResultRetrieveParams(TypedDict, total=False): - include_content: bool diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py deleted file mode 100644 index 2895923..0000000 --- a/src/docstrange/types/extract_async_params.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractAsyncParams"] - - -class ExtractAsyncParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - output_format: Required[str] - """Output format(s): markdown, html, json, csv.""" - - csv_options: str - """CSV extraction options""" - - custom_instructions: str - """Custom extraction instructions""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata types""" - - json_options: str - """JSON extraction options""" - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py deleted file mode 100644 index 380d386..0000000 --- a/src/docstrange/types/extract_batch_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ExtractBatchParams"] - - -class ExtractBatchParams(TypedDict, total=False): - files: Required[SequenceNotStr[FileTypes]] - """Files to process (max 50)""" - - output_format: Required[str] - - csv_options: str - - custom_instructions: str - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index a4d3ed3..ba021e0 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,34 +1,17 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional -from datetime import datetime -from typing_extensions import Literal from .._models import BaseModel -from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): - message: str - record_id: str - status: Literal["completed", "processing", "failed"] + status: str success: bool - created_at: Optional[datetime] = None - - file_size: Optional[int] = None - - filename: Optional[str] = None - - output_format: Optional[str] = None - - pages_processed: Optional[int] = None - - processing_time: Optional[float] = None - - result: Optional[ExtractionResult] = None + message: Optional[str] = None diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py deleted file mode 100644 index 52147de..0000000 --- a/src/docstrange/types/extract_stream_params.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractStreamParams"] - - -class ExtractStreamParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload""" - - output_format: Required[str] - """Output format(s)""" - - csv_options: str - - custom_instructions: str - - enable_streaming: bool - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py deleted file mode 100644 index 333ff97..0000000 --- a/src/docstrange/types/extract_stream_response.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import TypeAlias - -__all__ = ["ExtractStreamResponse"] - -ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 3208694..1f032ea 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -35,3 +35,4 @@ class ExtractSyncParams(TypedDict, total=False): """JSON extraction options""" prompt_mode: Literal["append", "replace"] + """append or replace""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py deleted file mode 100644 index 579500c..0000000 --- a/src/docstrange/types/extraction_format_result.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional - -from .._models import BaseModel -from .extraction_metadata import ExtractionMetadata - -__all__ = ["ExtractionFormatResult"] - - -class ExtractionFormatResult(BaseModel): - content: Union[str, object] - """Extracted content""" - - metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py deleted file mode 100644 index d206781..0000000 --- a/src/docstrange/types/extraction_metadata.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["ExtractionMetadata"] - - -class ExtractionMetadata(BaseModel): - bounding_boxes: Optional[object] = None - - confidence_score: Optional[object] = None diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py deleted file mode 100644 index 8cd1772..0000000 --- a/src/docstrange/types/extraction_result.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from pydantic import Field as FieldInfo - -from .._models import BaseModel -from .extraction_format_result import ExtractionFormatResult - -__all__ = ["ExtractionResult"] - - -class ExtractionResult(BaseModel): - csv: Optional[ExtractionFormatResult] = None - - html: Optional[ExtractionFormatResult] = None - - json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) - - markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py deleted file mode 100644 index 3c1b290..0000000 --- a/src/docstrange/types/file_classification_result.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional - -from .._models import BaseModel -from .page_classification import PageClassification - -__all__ = ["FileClassificationResult"] - - -class FileClassificationResult(BaseModel): - filename: str - - pages: List[PageClassification] - - total_pages: int - - error: Optional[str] = None - - processing_time: Optional[float] = None - - record_id: Optional[str] = None diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py deleted file mode 100644 index afc73a3..0000000 --- a/src/docstrange/types/page_classification.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["PageClassification"] - - -class PageClassification(BaseModel): - category: str - - confidence: int - - page_number: int - - reasoning: str - - identified_category: Optional[str] = None diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/extract/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py deleted file mode 100644 index ee1d5f5..0000000 --- a/tests/api_resources/extract/test_results.py +++ /dev/null @@ -1,205 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse -from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestResults: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_retrieve(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_retrieve(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_retrieve(self, client: Docstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list(self, client: Docstrange) -> None: - result = client.extract.results.list() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_list(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_list(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncResults: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - await async_client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py deleted file mode 100644 index 84bfced..0000000 --- a/tests/api_resources/test_chat.py +++ /dev/null @@ -1,171 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestChat: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_create_completion(self, client: Docstrange) -> None: - response = client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_create_completion(self, client: Docstrange) -> None: - with client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncChat: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: - response = await async_client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: - async with async_client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index d40e1fa..63539df 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse, BatchClassifyResponse +from docstrange.types import ClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,43 +17,6 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - classify = client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -97,43 +60,6 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index bd13f30..d911970 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,10 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ( - ExtractResponse, - BatchExtractResponse, -) +from docstrange.types import ExtractResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,163 +17,6 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_async(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_async(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="markdown", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream_with_all_params(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_raw_response_stream(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = response.parse() - stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_streaming_response_stream(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = response.parse() - stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -236,163 +76,6 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="markdown", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = await response.parse() - await stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = await response.parse() - await stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: From f6e304ad4aea0364c89273b1a17f53cf730c9f09 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:29:03 +0000 Subject: [PATCH 21/26] feat(api): api update --- .stats.yml | 8 +- README.md | 61 ++ api.md | 47 +- src/docstrange/_client.py | 41 +- src/docstrange/pagination.py | 67 ++ src/docstrange/resources/__init__.py | 14 + src/docstrange/resources/chat.py | 182 +++++ src/docstrange/resources/classify.py | 147 +++- src/docstrange/resources/extract.py | 256 ------- src/docstrange/resources/extract/__init__.py | 33 + src/docstrange/resources/extract/extract.py | 724 ++++++++++++++++++ src/docstrange/resources/extract/results.py | 290 +++++++ src/docstrange/types/__init__.py | 8 + .../types/batch_classify_response.py | 13 + .../types/batch_extract_response.py | 13 + .../types/chat_create_completion_params.py | 40 + src/docstrange/types/classify_batch_params.py | 17 + src/docstrange/types/extract/__init__.py | 7 + .../types/extract/extraction_list_response.py | 15 + .../types/extract/result_list_params.py | 17 + .../types/extract/result_retrieve_params.py | 11 + src/docstrange/types/extract_async_params.py | 37 + src/docstrange/types/extract_batch_params.py | 26 + src/docstrange/types/extract_stream_params.py | 35 + .../types/extract_stream_response.py | 7 + src/docstrange/types/extract_sync_params.py | 1 - tests/api_resources/extract/__init__.py | 1 + tests/api_resources/extract/test_results.py | 205 +++++ tests/api_resources/test_chat.py | 171 +++++ tests/api_resources/test_classify.py | 76 +- tests/api_resources/test_extract.py | 319 +++++++- 31 files changed, 2598 insertions(+), 291 deletions(-) create mode 100644 src/docstrange/pagination.py create mode 100644 src/docstrange/resources/chat.py delete mode 100644 src/docstrange/resources/extract.py create mode 100644 src/docstrange/resources/extract/__init__.py create mode 100644 src/docstrange/resources/extract/extract.py create mode 100644 src/docstrange/resources/extract/results.py create mode 100644 src/docstrange/types/batch_classify_response.py create mode 100644 src/docstrange/types/batch_extract_response.py create mode 100644 src/docstrange/types/chat_create_completion_params.py create mode 100644 src/docstrange/types/classify_batch_params.py create mode 100644 src/docstrange/types/extract/__init__.py create mode 100644 src/docstrange/types/extract/extraction_list_response.py create mode 100644 src/docstrange/types/extract/result_list_params.py create mode 100644 src/docstrange/types/extract/result_retrieve_params.py create mode 100644 src/docstrange/types/extract_async_params.py create mode 100644 src/docstrange/types/extract_batch_params.py create mode 100644 src/docstrange/types/extract_stream_params.py create mode 100644 src/docstrange/types/extract_stream_response.py create mode 100644 tests/api_resources/extract/__init__.py create mode 100644 tests/api_resources/extract/test_results.py create mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index e24fff1..2430e33 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-4a17ecf235fd346afe631381db80e46b26346368a323939de7b0741a5b60ce3d.yml -openapi_spec_hash: c46bca368a4f85e1a684b309e4129ef5 -config_hash: 6f0ab1fd6c4433590ee82d88ab5298a7 +configured_endpoints: 9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-61ca0326580cb90b4bc39c85c49cb2083d14ce9865ea272f2cb84cc7c639b0b2.yml +openapi_spec_hash: 6940490040b26eeb3c38bb2d7d9f00ce +config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 diff --git a/README.md b/README.md index 74c6d8b..e26fbd2 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,67 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Docstrange API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from docstrange import Docstrange + +client = Docstrange() + +all_results = [] +# Automatically fetches more pages as needed. +for result in client.extract.results.list(): + # Do something with result here + all_results.append(result) +print(all_results) +``` + +Or, asynchronously: + +```python +import asyncio +from docstrange import AsyncDocstrange + +client = AsyncDocstrange() + + +async def main() -> None: + all_results = [] + # Iterate through items across all pages, issuing requests as needed. + async for result in client.extract.results.list(): + all_results.append(result) + print(all_results) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.extract.results.list() +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.results)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.extract.results.list() +for result in first_page.results: + print(result) + +# Remove `await` for non-async usage. +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 0cac7c9..790cf4e 100644 --- a/api.md +++ b/api.md @@ -3,21 +3,62 @@ Types: ```python -from docstrange.types import ExtractResponse +from docstrange.types import ( + BatchExtractRequestBody, + BatchExtractResponse, + ExtractRequestBody, + ExtractResponse, + StreamExtractRequestBody, + ExtractStreamResponse, +) ``` Methods: -- client.extract.sync(\*\*params) -> ExtractResponse +- client.extract.async\_(\*\*params) -> ExtractResponse +- client.extract.batch(\*\*params) -> BatchExtractResponse +- client.extract.stream(\*\*params) -> str +- client.extract.sync(\*\*params) -> ExtractResponse + +## Results + +Types: + +```python +from docstrange.types.extract import ExtractionListResponse +``` + +Methods: + +- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse +- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[object] # Classify Types: ```python -from docstrange.types import ClassifyResponse +from docstrange.types import ( + BatchClassifyRequestBody, + BatchClassifyResponse, + ClassifyRequestBody, + ClassifyResponse, +) ``` Methods: +- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse + +# Chat + +Types: + +```python +from docstrange.types import ChatCompletionsRequest +``` + +Methods: + +- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 42d282d..4c5bcc4 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,9 +31,10 @@ ) if TYPE_CHECKING: - from .resources import extract, classify - from .resources.extract import ExtractResource, AsyncExtractResource + from .resources import chat, extract, classify + from .resources.chat import ChatResource, AsyncChatResource from .resources.classify import ClassifyResource, AsyncClassifyResource + from .resources.extract.extract import ExtractResource, AsyncExtractResource __all__ = [ "Timeout", @@ -114,6 +115,12 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) + @cached_property + def chat(self) -> ChatResource: + from .resources.chat import ChatResource + + return ChatResource(self) + @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -294,6 +301,12 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) + @cached_property + def chat(self) -> AsyncChatResource: + from .resources.chat import AsyncChatResource + + return AsyncChatResource(self) + @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -425,6 +438,12 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithRawResponse: + from .resources.chat import ChatResourceWithRawResponse + + return ChatResourceWithRawResponse(self._client.chat) + class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -444,6 +463,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithRawResponse: + from .resources.chat import AsyncChatResourceWithRawResponse + + return AsyncChatResourceWithRawResponse(self._client.chat) + class DocstrangeWithStreamedResponse: _client: Docstrange @@ -463,6 +488,12 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithStreamingResponse: + from .resources.chat import ChatResourceWithStreamingResponse + + return ChatResourceWithStreamingResponse(self._client.chat) + class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -482,6 +513,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: + from .resources.chat import AsyncChatResourceWithStreamingResponse + + return AsyncChatResourceWithStreamingResponse(self._client.chat) + Client = Docstrange diff --git a/src/docstrange/pagination.py b/src/docstrange/pagination.py new file mode 100644 index 0000000..e926095 --- /dev/null +++ b/src/docstrange/pagination.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional, cast +from typing_extensions import override + +from ._models import BaseModel +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["PageNumberPaginationPagination", "SyncPageNumberPagination", "AsyncPageNumberPagination"] + +_T = TypeVar("_T") + + +class PageNumberPaginationPagination(BaseModel): + has_next: Optional[bool] = None + + total_count: Optional[int] = None + + +class SyncPageNumberPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) + + +class AsyncPageNumberPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + results: List[_T] + pagination: Optional[PageNumberPaginationPagination] = None + + @override + def _get_page_items(self) -> List[_T]: + results = self.results + if not results: + return [] + return results + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_page = cast("int | None", self._options.params.get("page")) or 1 + + total_pages = None + if self.pagination is not None: + if self.pagination.total_count is not None: + total_pages = self.pagination.total_count + if total_pages is not None and last_page >= total_pages: + return None + + return PageInfo(params={"page": last_page + 1}) diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index 6d7ff55..a3172dd 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) from .extract import ( ExtractResource, AsyncExtractResource, @@ -30,4 +38,10 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py new file mode 100644 index 0000000..7e8899d --- /dev/null +++ b/src/docstrange/resources/chat.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..types import chat_create_completion_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + OpenAI-compatible chat completions endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + OpenAI-compatible chat completions endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_raw_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_raw_response_wrapper( + chat.create_completion, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_streamed_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_streamed_response_wrapper( + chat.create_completion, + ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index d7853b4..ded77af 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,13 +2,11 @@ from __future__ import annotations -from typing import Mapping, cast - import httpx -from ..types import classify_sync_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..types import classify_sync_params, classify_batch_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -19,6 +17,7 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse +from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -43,6 +42,53 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) + def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50). + + Args: + categories: JSON array of category objects + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/classify/batch", + body=maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + def sync( self, *, @@ -71,21 +117,19 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -113,6 +157,53 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) + async def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50). + + Args: + categories: JSON array of category objects + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/classify/batch", + body=await async_maybe_transform( + { + "categories": categories, + "files": files, + }, + classify_batch_params.ClassifyBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + async def sync( self, *, @@ -141,21 +232,19 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "categories": categories, - "file": file, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), - files=files, + body=await async_maybe_transform( + { + "categories": categories, + "file": file, + }, + classify_sync_params.ClassifySyncParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -167,6 +256,9 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_raw_response_wrapper( + classify.batch, + ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -176,6 +268,9 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_raw_response_wrapper( + classify.batch, + ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -185,6 +280,9 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_streamed_response_wrapper( + classify.batch, + ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -194,6 +292,9 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_streamed_response_wrapper( + classify.batch, + ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract.py b/src/docstrange/resources/extract.py deleted file mode 100644 index 5b896e5..0000000 --- a/src/docstrange/resources/extract.py +++ /dev/null @@ -1,256 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Mapping, cast -from typing_extensions import Literal - -import httpx - -from ..types import extract_sync_params -from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.extract_response import ExtractResponse - -__all__ = ["ExtractResource", "AsyncExtractResource"] - - -class ExtractResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ExtractResourceWithStreamingResponse(self) - - def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - prompt_mode: append or replace - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/sync", - body=maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class AsyncExtractResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncExtractResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncExtractResourceWithStreamingResponse(self) - - async def sync( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - prompt_mode: append or replace - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - -class ExtractResourceWithRawResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_raw_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithRawResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_raw_response_wrapper( - extract.sync, - ) - - -class ExtractResourceWithStreamingResponse: - def __init__(self, extract: ExtractResource) -> None: - self._extract = extract - - self.sync = to_streamed_response_wrapper( - extract.sync, - ) - - -class AsyncExtractResourceWithStreamingResponse: - def __init__(self, extract: AsyncExtractResource) -> None: - self._extract = extract - - self.sync = async_to_streamed_response_wrapper( - extract.sync, - ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py new file mode 100644 index 0000000..22cd257 --- /dev/null +++ b/src/docstrange/resources/extract/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .extract import ( + ExtractResource, + AsyncExtractResource, + ExtractResourceWithRawResponse, + AsyncExtractResourceWithRawResponse, + ExtractResourceWithStreamingResponse, + AsyncExtractResourceWithStreamingResponse, +) +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) + +__all__ = [ + "ResultsResource", + "AsyncResultsResource", + "ResultsResourceWithRawResponse", + "AsyncResultsResourceWithRawResponse", + "ResultsResourceWithStreamingResponse", + "AsyncResultsResourceWithStreamingResponse", + "ExtractResource", + "AsyncExtractResource", + "ExtractResourceWithRawResponse", + "AsyncExtractResourceWithRawResponse", + "ExtractResourceWithStreamingResponse", + "AsyncExtractResourceWithStreamingResponse", +] diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py new file mode 100644 index 0000000..335f013 --- /dev/null +++ b/src/docstrange/resources/extract/extract.py @@ -0,0 +1,724 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.extract_response import ExtractResponse +from ...types.batch_extract_response import BatchExtractResponse +from ...types.extract_stream_response import ExtractStreamResponse + +__all__ = ["ExtractResource", "AsyncExtractResource"] + + +class ExtractResource(SyncAPIResource): + @cached_property + def results(self) -> ResultsResource: + return ResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> ExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ExtractResourceWithStreamingResponse(self) + + def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Queue a document for background processing. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/async", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """ + Process multiple files asynchronously (max 50). + + Args: + files: Files to process (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/batch", + body=maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ExtractStreamResponse]: + """ + Stream extraction results via SSE. + + Args: + file: File to upload + + output_format: Output format(s) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/stream", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=Stream[ExtractStreamResponse], + ) + + def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class AsyncExtractResource(AsyncAPIResource): + @cached_property + def results(self) -> AsyncResultsResource: + return AsyncResultsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtractResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncExtractResourceWithStreamingResponse(self) + + async def async_( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Queue a document for background processing. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/async", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_async_params.ExtractAsyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + async def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """ + Process multiple files asynchronously (max 50). + + Args: + files: Files to process (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/batch", + body=await async_maybe_transform( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_batch_params.ExtractBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + async def stream( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ExtractStreamResponse]: + """ + Stream extraction results via SSE. + + Args: + file: File to upload + + output_format: Output format(s) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/stream", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_stream_params.ExtractStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + stream=True, + stream_cls=AsyncStream[ExtractStreamResponse], + ) + + async def sync( + self, + *, + file: FileTypes, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Process a document and return extracted content immediately. + + Args: + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + output_format: Output format(s): markdown, html, json, csv. + + csv_options: CSV extraction options + + custom_instructions: Custom extraction instructions + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata types + + json_options: JSON extraction options + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform( + { + "file": file, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + }, + extract_sync_params.ExtractSyncParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + +class ExtractResourceWithRawResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_raw_response_wrapper( + extract.async_, + ) + self.batch = to_raw_response_wrapper( + extract.batch, + ) + self.stream = to_raw_response_wrapper( + extract.stream, + ) + self.sync = to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithRawResponse: + return ResultsResourceWithRawResponse(self._extract.results) + + +class AsyncExtractResourceWithRawResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_raw_response_wrapper( + extract.async_, + ) + self.batch = async_to_raw_response_wrapper( + extract.batch, + ) + self.stream = async_to_raw_response_wrapper( + extract.stream, + ) + self.sync = async_to_raw_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithRawResponse: + return AsyncResultsResourceWithRawResponse(self._extract.results) + + +class ExtractResourceWithStreamingResponse: + def __init__(self, extract: ExtractResource) -> None: + self._extract = extract + + self.async_ = to_streamed_response_wrapper( + extract.async_, + ) + self.batch = to_streamed_response_wrapper( + extract.batch, + ) + self.stream = to_streamed_response_wrapper( + extract.stream, + ) + self.sync = to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> ResultsResourceWithStreamingResponse: + return ResultsResourceWithStreamingResponse(self._extract.results) + + +class AsyncExtractResourceWithStreamingResponse: + def __init__(self, extract: AsyncExtractResource) -> None: + self._extract = extract + + self.async_ = async_to_streamed_response_wrapper( + extract.async_, + ) + self.batch = async_to_streamed_response_wrapper( + extract.batch, + ) + self.stream = async_to_streamed_response_wrapper( + extract.stream, + ) + self.sync = async_to_streamed_response_wrapper( + extract.sync, + ) + + @cached_property + def results(self) -> AsyncResultsResourceWithStreamingResponse: + return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py new file mode 100644 index 0000000..dab80bf --- /dev/null +++ b/src/docstrange/resources/extract/results.py @@ -0,0 +1,290 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination +from ..._base_client import AsyncPaginator, make_request_options +from ...types.extract import result_list_params, result_retrieve_params +from ...types.extract_response import ExtractResponse + +__all__ = ["ResultsResource", "AsyncResultsResource"] + + +class ResultsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ResultsResourceWithStreamingResponse(self) + + def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPageNumberPagination[object]: + """ + List all extraction jobs (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=SyncPageNumberPagination[object], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=object, + ) + + +class AsyncResultsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncResultsResourceWithStreamingResponse(self) + + async def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return await self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[object, AsyncPageNumberPagination[object]]: + """ + List all extraction jobs (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=AsyncPageNumberPagination[object], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=object, + ) + + +class ResultsResourceWithRawResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_raw_response_wrapper( + results.retrieve, + ) + self.list = to_raw_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithRawResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_raw_response_wrapper( + results.retrieve, + ) + self.list = async_to_raw_response_wrapper( + results.list, + ) + + +class ResultsResourceWithStreamingResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_streamed_response_wrapper( + results.retrieve, + ) + self.list = to_streamed_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithStreamingResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_streamed_response_wrapper( + results.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + results.list, + ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index fac3955..f7946ad 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -6,3 +6,11 @@ from .classify_response import ClassifyResponse as ClassifyResponse from .extract_sync_params import ExtractSyncParams as ExtractSyncParams from .classify_sync_params import ClassifySyncParams as ClassifySyncParams +from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams +from .extract_batch_params import ExtractBatchParams as ExtractBatchParams +from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams +from .extract_stream_params import ExtractStreamParams as ExtractStreamParams +from .batch_extract_response import BatchExtractResponse as BatchExtractResponse +from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse +from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse +from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py new file mode 100644 index 0000000..7b26938 --- /dev/null +++ b/src/docstrange/types/batch_classify_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchClassifyResponse"] + + +class BatchClassifyResponse(BaseModel): + batch_id: str + + message: str + + success: bool diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py new file mode 100644 index 0000000..64b21bf --- /dev/null +++ b/src/docstrange/types/batch_extract_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchExtractResponse"] + + +class BatchExtractResponse(BaseModel): + batch_id: str + + message: str + + success: bool diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py new file mode 100644 index 0000000..eea9be6 --- /dev/null +++ b/src/docstrange/types/chat_create_completion_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] + + +class ChatCreateCompletionParams(TypedDict, total=False): + messages: Required[Iterable[Message]] + + model: Required[str] + + stream: bool + + +class MessageContentFileURL(TypedDict, total=False): + url: str + + +class MessageContentImageURL(TypedDict, total=False): + url: str + + +class MessageContent(TypedDict, total=False): + file_url: MessageContentFileURL + + image_url: MessageContentImageURL + + text: str + + type: str + + +class Message(TypedDict, total=False): + content: Required[Iterable[MessageContent]] + + role: Required[str] diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py new file mode 100644 index 0000000..1f23cd0 --- /dev/null +++ b/src/docstrange/types/classify_batch_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ClassifyBatchParams"] + + +class ClassifyBatchParams(TypedDict, total=False): + categories: Required[str] + """JSON array of category objects""" + + files: Required[SequenceNotStr[FileTypes]] + """Files to classify (max 50)""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py new file mode 100644 index 0000000..692c8de --- /dev/null +++ b/src/docstrange/types/extract/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .result_list_params import ResultListParams as ResultListParams +from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams +from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py new file mode 100644 index 0000000..60f049a --- /dev/null +++ b/src/docstrange/types/extract/extraction_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["ExtractionListResponse"] + + +class ExtractionListResponse(BaseModel): + pagination: object + + results: List[object] + + success: bool diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py new file mode 100644 index 0000000..7dfe38a --- /dev/null +++ b/src/docstrange/types/extract/result_list_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ResultListParams"] + + +class ResultListParams(TypedDict, total=False): + page: int + + page_size: int + + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + + sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py new file mode 100644 index 0000000..1de1a80 --- /dev/null +++ b/src/docstrange/types/extract/result_retrieve_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ResultRetrieveParams"] + + +class ResultRetrieveParams(TypedDict, total=False): + include_content: bool diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py new file mode 100644 index 0000000..2895923 --- /dev/null +++ b/src/docstrange/types/extract_async_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractAsyncParams"] + + +class ExtractAsyncParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + output_format: Required[str] + """Output format(s): markdown, html, json, csv.""" + + csv_options: str + """CSV extraction options""" + + custom_instructions: str + """Custom extraction instructions""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata types""" + + json_options: str + """JSON extraction options""" + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py new file mode 100644 index 0000000..380d386 --- /dev/null +++ b/src/docstrange/types/extract_batch_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ExtractBatchParams"] + + +class ExtractBatchParams(TypedDict, total=False): + files: Required[SequenceNotStr[FileTypes]] + """Files to process (max 50)""" + + output_format: Required[str] + + csv_options: str + + custom_instructions: str + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py new file mode 100644 index 0000000..52147de --- /dev/null +++ b/src/docstrange/types/extract_stream_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractStreamParams"] + + +class ExtractStreamParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload""" + + output_format: Required[str] + """Output format(s)""" + + csv_options: str + + custom_instructions: str + + enable_streaming: bool + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py new file mode 100644 index 0000000..333ff97 --- /dev/null +++ b/src/docstrange/types/extract_stream_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ExtractStreamResponse"] + +ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 1f032ea..3208694 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -35,4 +35,3 @@ class ExtractSyncParams(TypedDict, total=False): """JSON extraction options""" prompt_mode: Literal["append", "replace"] - """append or replace""" diff --git a/tests/api_resources/extract/__init__.py b/tests/api_resources/extract/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/extract/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py new file mode 100644 index 0000000..0b67e0f --- /dev/null +++ b/tests/api_resources/extract/test_results.py @@ -0,0 +1,205 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type +from docstrange.types import ExtractResponse +from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResults: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Docstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Docstrange) -> None: + result = client.extract.results.list() + assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncResults: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + await async_client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list() + assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py new file mode 100644 index 0000000..84bfced --- /dev/null +++ b/tests/api_resources/test_chat.py @@ -0,0 +1,171 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestChat: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "type", + } + ], + "role": "role", + } + ], + model="model", + stream=True, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_completion(self, client: Docstrange) -> None: + response = client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_completion(self, client: Docstrange) -> None: + with client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncChat: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "type", + } + ], + "role": "role", + } + ], + model="model", + stream=True, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: + response = await async_client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: + async with async_client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}], + "role": "role", + } + ], + model="model", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index 63539df..d40e1fa 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse +from docstrange.types import ClassifyResponse, BatchClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +17,43 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + classify = client.classify.batch( + categories="categories", + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.classify.with_raw_response.batch( + categories="categories", + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.classify.with_streaming_response.batch( + categories="categories", + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -60,6 +97,43 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + classify = await async_client.classify.batch( + categories="categories", + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.classify.with_raw_response.batch( + categories="categories", + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.classify.with_streaming_response.batch( + categories="categories", + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index d911970..bd13f30 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,7 +9,10 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse +from docstrange.types import ( + ExtractResponse, + BatchExtractResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +20,163 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_async(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_async(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream_with_all_params(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_raw_response_stream(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_streaming_response_stream(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -76,6 +236,163 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.async_( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.async_( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + file=b"raw file contents", + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.stream( + file=b"raw file contents", + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.stream( + file=b"raw file contents", + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: From 5d5fe8c9980e169a262eeed5601a568b8688a666 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:30:40 +0000 Subject: [PATCH 22/26] feat(api): api update --- .stats.yml | 6 +- README.md | 61 --- api.md | 45 +- src/docstrange/_client.py | 39 +- src/docstrange/resources/__init__.py | 14 - src/docstrange/resources/chat.py | 182 ------- src/docstrange/resources/classify.py | 111 +--- src/docstrange/resources/extract/__init__.py | 14 - src/docstrange/resources/extract/extract.py | 485 +----------------- src/docstrange/resources/extract/results.py | 290 ----------- src/docstrange/types/__init__.py | 8 - .../types/batch_classify_response.py | 13 - .../types/batch_extract_response.py | 13 - .../types/chat_create_completion_params.py | 40 -- src/docstrange/types/classify_batch_params.py | 17 - src/docstrange/types/extract/__init__.py | 4 - .../types/extract/extraction_list_response.py | 15 - .../types/extract/result_list_params.py | 17 - .../types/extract/result_retrieve_params.py | 11 - src/docstrange/types/extract_async_params.py | 37 -- src/docstrange/types/extract_batch_params.py | 26 - src/docstrange/types/extract_stream_params.py | 35 -- .../types/extract_stream_response.py | 7 - src/docstrange/types/extract_sync_params.py | 1 + tests/api_resources/extract/test_results.py | 205 -------- tests/api_resources/test_chat.py | 171 ------ tests/api_resources/test_classify.py | 76 +-- tests/api_resources/test_extract.py | 319 +----------- 28 files changed, 17 insertions(+), 2245 deletions(-) delete mode 100644 src/docstrange/resources/chat.py delete mode 100644 src/docstrange/resources/extract/results.py delete mode 100644 src/docstrange/types/batch_classify_response.py delete mode 100644 src/docstrange/types/batch_extract_response.py delete mode 100644 src/docstrange/types/chat_create_completion_params.py delete mode 100644 src/docstrange/types/classify_batch_params.py delete mode 100644 src/docstrange/types/extract/extraction_list_response.py delete mode 100644 src/docstrange/types/extract/result_list_params.py delete mode 100644 src/docstrange/types/extract/result_retrieve_params.py delete mode 100644 src/docstrange/types/extract_async_params.py delete mode 100644 src/docstrange/types/extract_batch_params.py delete mode 100644 src/docstrange/types/extract_stream_params.py delete mode 100644 src/docstrange/types/extract_stream_response.py delete mode 100644 tests/api_resources/extract/test_results.py delete mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index 2430e33..f839396 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 9 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-61ca0326580cb90b4bc39c85c49cb2083d14ce9865ea272f2cb84cc7c639b0b2.yml -openapi_spec_hash: 6940490040b26eeb3c38bb2d7d9f00ce +configured_endpoints: 2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-75dd621006675868b0701738226a045f2e9d7211242fece922f06478bef66738.yml +openapi_spec_hash: 47cb78aa2caa018fe58742fc19b0a49c config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 diff --git a/README.md b/README.md index e26fbd2..74c6d8b 100644 --- a/README.md +++ b/README.md @@ -115,67 +115,6 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## Pagination - -List methods in the Docstrange API are paginated. - -This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: - -```python -from docstrange import Docstrange - -client = Docstrange() - -all_results = [] -# Automatically fetches more pages as needed. -for result in client.extract.results.list(): - # Do something with result here - all_results.append(result) -print(all_results) -``` - -Or, asynchronously: - -```python -import asyncio -from docstrange import AsyncDocstrange - -client = AsyncDocstrange() - - -async def main() -> None: - all_results = [] - # Iterate through items across all pages, issuing requests as needed. - async for result in client.extract.results.list(): - all_results.append(result) - print(all_results) - - -asyncio.run(main()) -``` - -Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: - -```python -first_page = await client.extract.results.list() -if first_page.has_next_page(): - print(f"will fetch next page using these details: {first_page.next_page_info()}") - next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.results)}") - -# Remove `await` for non-async usage. -``` - -Or just work directly with the returned data: - -```python -first_page = await client.extract.results.list() -for result in first_page.results: - print(result) - -# Remove `await` for non-async usage. -``` - ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. diff --git a/api.md b/api.md index 790cf4e..96e2318 100644 --- a/api.md +++ b/api.md @@ -3,62 +3,21 @@ Types: ```python -from docstrange.types import ( - BatchExtractRequestBody, - BatchExtractResponse, - ExtractRequestBody, - ExtractResponse, - StreamExtractRequestBody, - ExtractStreamResponse, -) +from docstrange.types import ExtractRequestBody, ExtractResponse ``` Methods: -- client.extract.async\_(\*\*params) -> ExtractResponse -- client.extract.batch(\*\*params) -> BatchExtractResponse -- client.extract.stream(\*\*params) -> str - client.extract.sync(\*\*params) -> ExtractResponse -## Results - -Types: - -```python -from docstrange.types.extract import ExtractionListResponse -``` - -Methods: - -- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse -- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[object] - # Classify Types: ```python -from docstrange.types import ( - BatchClassifyRequestBody, - BatchClassifyResponse, - ClassifyRequestBody, - ClassifyResponse, -) +from docstrange.types import ClassifyRequestBody, ClassifyResponse ``` Methods: -- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse - -# Chat - -Types: - -```python -from docstrange.types import ChatCompletionsRequest -``` - -Methods: - -- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 4c5bcc4..271a014 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,8 +31,7 @@ ) if TYPE_CHECKING: - from .resources import chat, extract, classify - from .resources.chat import ChatResource, AsyncChatResource + from .resources import extract, classify from .resources.classify import ClassifyResource, AsyncClassifyResource from .resources.extract.extract import ExtractResource, AsyncExtractResource @@ -115,12 +114,6 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) - @cached_property - def chat(self) -> ChatResource: - from .resources.chat import ChatResource - - return ChatResource(self) - @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -301,12 +294,6 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) - @cached_property - def chat(self) -> AsyncChatResource: - from .resources.chat import AsyncChatResource - - return AsyncChatResource(self) - @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -438,12 +425,6 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithRawResponse: - from .resources.chat import ChatResourceWithRawResponse - - return ChatResourceWithRawResponse(self._client.chat) - class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -463,12 +444,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithRawResponse: - from .resources.chat import AsyncChatResourceWithRawResponse - - return AsyncChatResourceWithRawResponse(self._client.chat) - class DocstrangeWithStreamedResponse: _client: Docstrange @@ -488,12 +463,6 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.ChatResourceWithStreamingResponse: - from .resources.chat import ChatResourceWithStreamingResponse - - return ChatResourceWithStreamingResponse(self._client.chat) - class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -513,12 +482,6 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) - @cached_property - def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: - from .resources.chat import AsyncChatResourceWithStreamingResponse - - return AsyncChatResourceWithStreamingResponse(self._client.chat) - Client = Docstrange diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index a3172dd..6d7ff55 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .chat import ( - ChatResource, - AsyncChatResource, - ChatResourceWithRawResponse, - AsyncChatResourceWithRawResponse, - ChatResourceWithStreamingResponse, - AsyncChatResourceWithStreamingResponse, -) from .extract import ( ExtractResource, AsyncExtractResource, @@ -38,10 +30,4 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", - "ChatResource", - "AsyncChatResource", - "ChatResourceWithRawResponse", - "AsyncChatResourceWithRawResponse", - "ChatResourceWithStreamingResponse", - "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py deleted file mode 100644 index 7e8899d..0000000 --- a/src/docstrange/resources/chat.py +++ /dev/null @@ -1,182 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable - -import httpx - -from ..types import chat_create_completion_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options - -__all__ = ["ChatResource", "AsyncChatResource"] - - -class ChatResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ChatResourceWithStreamingResponse(self) - - def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/chat/completions", - body=maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class AsyncChatResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncChatResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncChatResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncChatResourceWithStreamingResponse(self) - - async def create_completion( - self, - *, - messages: Iterable[chat_create_completion_params.Message], - model: str, - stream: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> object: - """ - OpenAI-compatible chat completions endpoint. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/chat/completions", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "stream": stream, - }, - chat_create_completion_params.ChatCreateCompletionParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=object, - ) - - -class ChatResourceWithRawResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_raw_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithRawResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_raw_response_wrapper( - chat.create_completion, - ) - - -class ChatResourceWithStreamingResponse: - def __init__(self, chat: ChatResource) -> None: - self._chat = chat - - self.create_completion = to_streamed_response_wrapper( - chat.create_completion, - ) - - -class AsyncChatResourceWithStreamingResponse: - def __init__(self, chat: AsyncChatResource) -> None: - self._chat = chat - - self.create_completion = async_to_streamed_response_wrapper( - chat.create_completion, - ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index ded77af..382aa89 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -4,8 +4,8 @@ import httpx -from ..types import classify_sync_params, classify_batch_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from ..types import classify_sync_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -17,7 +17,6 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse -from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -42,53 +41,6 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) - def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50). - - Args: - categories: JSON array of category objects - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/classify/batch", - body=maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - def sync( self, *, @@ -157,53 +109,6 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) - async def batch( - self, - *, - categories: str, - files: SequenceNotStr[FileTypes], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchClassifyResponse: - """ - Classify multiple documents asynchronously (max 50). - - Args: - categories: JSON array of category objects - - files: Files to classify (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/classify/batch", - body=await async_maybe_transform( - { - "categories": categories, - "files": files, - }, - classify_batch_params.ClassifyBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchClassifyResponse, - ) - async def sync( self, *, @@ -256,9 +161,6 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_raw_response_wrapper( - classify.batch, - ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -268,9 +170,6 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_raw_response_wrapper( - classify.batch, - ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -280,9 +179,6 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify - self.batch = to_streamed_response_wrapper( - classify.batch, - ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -292,9 +188,6 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify - self.batch = async_to_streamed_response_wrapper( - classify.batch, - ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py index 22cd257..a0fee27 100644 --- a/src/docstrange/resources/extract/__init__.py +++ b/src/docstrange/resources/extract/__init__.py @@ -8,22 +8,8 @@ ExtractResourceWithStreamingResponse, AsyncExtractResourceWithStreamingResponse, ) -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) __all__ = [ - "ResultsResource", - "AsyncResultsResource", - "ResultsResourceWithRawResponse", - "AsyncResultsResourceWithRawResponse", - "ResultsResourceWithStreamingResponse", - "AsyncResultsResourceWithStreamingResponse", "ExtractResource", "AsyncExtractResource", "ExtractResourceWithRawResponse", diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py index 335f013..f5b48d9 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -6,26 +6,8 @@ import httpx -from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params -from .results import ( - ResultsResource, - AsyncResultsResource, - ResultsResourceWithRawResponse, - AsyncResultsResourceWithRawResponse, - ResultsResourceWithStreamingResponse, - AsyncResultsResourceWithStreamingResponse, -) -from ..._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) +from ...types import extract_sync_params +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -35,20 +17,13 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._streaming import Stream, AsyncStream from ..._base_client import make_request_options from ...types.extract_response import ExtractResponse -from ...types.batch_extract_response import BatchExtractResponse -from ...types.extract_stream_response import ExtractStreamResponse __all__ = ["ExtractResource", "AsyncExtractResource"] class ExtractResource(SyncAPIResource): - @cached_property - def results(self) -> ResultsResource: - return ResultsResource(self._client) - @cached_property def with_raw_response(self) -> ExtractResourceWithRawResponse: """ @@ -68,204 +43,6 @@ def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: """ return ExtractResourceWithStreamingResponse(self) - def async_( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Queue a document for background processing. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/async", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """ - Process multiple files asynchronously (max 50). - - Args: - files: Files to process (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/batch", - body=maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[ExtractStreamResponse]: - """ - Stream extraction results via SSE. - - Args: - file: File to upload - - output_format: Output format(s) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/api/v1/extract/stream", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=Stream[ExtractStreamResponse], - ) - def sync( self, *, @@ -305,6 +82,8 @@ def sync( json_options: JSON extraction options + prompt_mode: append or replace + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -341,10 +120,6 @@ def sync( class AsyncExtractResource(AsyncAPIResource): - @cached_property - def results(self) -> AsyncResultsResource: - return AsyncResultsResource(self._client) - @cached_property def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: """ @@ -364,204 +139,6 @@ def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: """ return AsyncExtractResourceWithStreamingResponse(self) - async def async_( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Queue a document for background processing. - - Args: - file: File to upload (PDF, Word, Excel, PowerPoint, images) - - output_format: Output format(s): markdown, html, json, csv. - - csv_options: CSV extraction options - - custom_instructions: Custom extraction instructions - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - include_metadata: Comma-separated metadata types - - json_options: JSON extraction options - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/async", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_async_params.ExtractAsyncParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ExtractResponse, - ) - - async def batch( - self, - *, - files: SequenceNotStr[FileTypes], - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BatchExtractResponse: - """ - Process multiple files asynchronously (max 50). - - Args: - files: Files to process (max 50) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/batch", - body=await async_maybe_transform( - { - "files": files, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_batch_params.ExtractBatchParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BatchExtractResponse, - ) - - async def stream( - self, - *, - file: FileTypes, - output_format: str, - csv_options: str | Omit = omit, - custom_instructions: str | Omit = omit, - enable_streaming: bool | Omit = omit, - file_base64: str | Omit = omit, - file_url: str | Omit = omit, - include_metadata: str | Omit = omit, - json_options: str | Omit = omit, - prompt_mode: Literal["append", "replace"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[ExtractStreamResponse]: - """ - Stream extraction results via SSE. - - Args: - file: File to upload - - output_format: Output format(s) - - file_base64: Base64-encoded file content - - file_url: URL to download file from - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/api/v1/extract/stream", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "enable_streaming": enable_streaming, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_stream_params.ExtractStreamParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=str, - stream=True, - stream_cls=AsyncStream[ExtractStreamResponse], - ) - async def sync( self, *, @@ -601,6 +178,8 @@ async def sync( json_options: JSON extraction options + prompt_mode: append or replace + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -640,85 +219,33 @@ class ExtractResourceWithRawResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract - self.async_ = to_raw_response_wrapper( - extract.async_, - ) - self.batch = to_raw_response_wrapper( - extract.batch, - ) - self.stream = to_raw_response_wrapper( - extract.stream, - ) self.sync = to_raw_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> ResultsResourceWithRawResponse: - return ResultsResourceWithRawResponse(self._extract.results) - class AsyncExtractResourceWithRawResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract - self.async_ = async_to_raw_response_wrapper( - extract.async_, - ) - self.batch = async_to_raw_response_wrapper( - extract.batch, - ) - self.stream = async_to_raw_response_wrapper( - extract.stream, - ) self.sync = async_to_raw_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> AsyncResultsResourceWithRawResponse: - return AsyncResultsResourceWithRawResponse(self._extract.results) - class ExtractResourceWithStreamingResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract - self.async_ = to_streamed_response_wrapper( - extract.async_, - ) - self.batch = to_streamed_response_wrapper( - extract.batch, - ) - self.stream = to_streamed_response_wrapper( - extract.stream, - ) self.sync = to_streamed_response_wrapper( extract.sync, ) - @cached_property - def results(self) -> ResultsResourceWithStreamingResponse: - return ResultsResourceWithStreamingResponse(self._extract.results) - class AsyncExtractResourceWithStreamingResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract - self.async_ = async_to_streamed_response_wrapper( - extract.async_, - ) - self.batch = async_to_streamed_response_wrapper( - extract.batch, - ) - self.stream = async_to_streamed_response_wrapper( - extract.stream, - ) self.sync = async_to_streamed_response_wrapper( extract.sync, ) - - @cached_property - def results(self) -> AsyncResultsResourceWithStreamingResponse: - return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py deleted file mode 100644 index dab80bf..0000000 --- a/src/docstrange/resources/extract/results.py +++ /dev/null @@ -1,290 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination -from ..._base_client import AsyncPaginator, make_request_options -from ...types.extract import result_list_params, result_retrieve_params -from ...types.extract_response import ExtractResponse - -__all__ = ["ResultsResource", "AsyncResultsResource"] - - -class ResultsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return ResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return ResultsResourceWithStreamingResponse(self) - - def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageNumberPagination[object]: - """ - List all extraction jobs (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=SyncPageNumberPagination[object], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=object, - ) - - -class AsyncResultsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers - """ - return AsyncResultsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response - """ - return AsyncResultsResourceWithStreamingResponse(self) - - async def retrieve( - self, - record_id: str, - *, - include_content: bool | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExtractResponse: - """ - Retrieve status and result of an extraction job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not record_id: - raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") - return await self._get( - f"/api/v1/extract/results/{record_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams - ), - ), - cast_to=ExtractResponse, - ) - - def list( - self, - *, - page: int | Omit = omit, - page_size: int | Omit = omit, - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - | Omit = omit, - sort_order: Literal["asc", "desc"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[object, AsyncPageNumberPagination[object]]: - """ - List all extraction jobs (paginated). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/api/v1/extract/results", - page=AsyncPageNumberPagination[object], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "page": page, - "page_size": page_size, - "sort_by": sort_by, - "sort_order": sort_order, - }, - result_list_params.ResultListParams, - ), - ), - model=object, - ) - - -class ResultsResourceWithRawResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_raw_response_wrapper( - results.retrieve, - ) - self.list = to_raw_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithRawResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_raw_response_wrapper( - results.retrieve, - ) - self.list = async_to_raw_response_wrapper( - results.list, - ) - - -class ResultsResourceWithStreamingResponse: - def __init__(self, results: ResultsResource) -> None: - self._results = results - - self.retrieve = to_streamed_response_wrapper( - results.retrieve, - ) - self.list = to_streamed_response_wrapper( - results.list, - ) - - -class AsyncResultsResourceWithStreamingResponse: - def __init__(self, results: AsyncResultsResource) -> None: - self._results = results - - self.retrieve = async_to_streamed_response_wrapper( - results.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - results.list, - ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index f7946ad..fac3955 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -6,11 +6,3 @@ from .classify_response import ClassifyResponse as ClassifyResponse from .extract_sync_params import ExtractSyncParams as ExtractSyncParams from .classify_sync_params import ClassifySyncParams as ClassifySyncParams -from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams -from .extract_batch_params import ExtractBatchParams as ExtractBatchParams -from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams -from .extract_stream_params import ExtractStreamParams as ExtractStreamParams -from .batch_extract_response import BatchExtractResponse as BatchExtractResponse -from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse -from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse -from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py deleted file mode 100644 index 7b26938..0000000 --- a/src/docstrange/types/batch_classify_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["BatchClassifyResponse"] - - -class BatchClassifyResponse(BaseModel): - batch_id: str - - message: str - - success: bool diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py deleted file mode 100644 index 64b21bf..0000000 --- a/src/docstrange/types/batch_extract_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["BatchExtractResponse"] - - -class BatchExtractResponse(BaseModel): - batch_id: str - - message: str - - success: bool diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py deleted file mode 100644 index eea9be6..0000000 --- a/src/docstrange/types/chat_create_completion_params.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, TypedDict - -__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] - - -class ChatCreateCompletionParams(TypedDict, total=False): - messages: Required[Iterable[Message]] - - model: Required[str] - - stream: bool - - -class MessageContentFileURL(TypedDict, total=False): - url: str - - -class MessageContentImageURL(TypedDict, total=False): - url: str - - -class MessageContent(TypedDict, total=False): - file_url: MessageContentFileURL - - image_url: MessageContentImageURL - - text: str - - type: str - - -class Message(TypedDict, total=False): - content: Required[Iterable[MessageContent]] - - role: Required[str] diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py deleted file mode 100644 index 1f23cd0..0000000 --- a/src/docstrange/types/classify_batch_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ClassifyBatchParams"] - - -class ClassifyBatchParams(TypedDict, total=False): - categories: Required[str] - """JSON array of category objects""" - - files: Required[SequenceNotStr[FileTypes]] - """Files to classify (max 50)""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py index 692c8de..f8ee8b1 100644 --- a/src/docstrange/types/extract/__init__.py +++ b/src/docstrange/types/extract/__init__.py @@ -1,7 +1,3 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations - -from .result_list_params import ResultListParams as ResultListParams -from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams -from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py deleted file mode 100644 index 60f049a..0000000 --- a/src/docstrange/types/extract/extraction_list_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel - -__all__ = ["ExtractionListResponse"] - - -class ExtractionListResponse(BaseModel): - pagination: object - - results: List[object] - - success: bool diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py deleted file mode 100644 index 7dfe38a..0000000 --- a/src/docstrange/types/extract/result_list_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, TypedDict - -__all__ = ["ResultListParams"] - - -class ResultListParams(TypedDict, total=False): - page: int - - page_size: int - - sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] - - sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py deleted file mode 100644 index 1de1a80..0000000 --- a/src/docstrange/types/extract/result_retrieve_params.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["ResultRetrieveParams"] - - -class ResultRetrieveParams(TypedDict, total=False): - include_content: bool diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py deleted file mode 100644 index 2895923..0000000 --- a/src/docstrange/types/extract_async_params.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractAsyncParams"] - - -class ExtractAsyncParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - - output_format: Required[str] - """Output format(s): markdown, html, json, csv.""" - - csv_options: str - """CSV extraction options""" - - custom_instructions: str - """Custom extraction instructions""" - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - """Comma-separated metadata types""" - - json_options: str - """JSON extraction options""" - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py deleted file mode 100644 index 380d386..0000000 --- a/src/docstrange/types/extract_batch_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes, SequenceNotStr - -__all__ = ["ExtractBatchParams"] - - -class ExtractBatchParams(TypedDict, total=False): - files: Required[SequenceNotStr[FileTypes]] - """Files to process (max 50)""" - - output_format: Required[str] - - csv_options: str - - custom_instructions: str - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py deleted file mode 100644 index 52147de..0000000 --- a/src/docstrange/types/extract_stream_params.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .._types import FileTypes - -__all__ = ["ExtractStreamParams"] - - -class ExtractStreamParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload""" - - output_format: Required[str] - """Output format(s)""" - - csv_options: str - - custom_instructions: str - - enable_streaming: bool - - file_base64: str - """Base64-encoded file content""" - - file_url: str - """URL to download file from""" - - include_metadata: str - - json_options: str - - prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py deleted file mode 100644 index 333ff97..0000000 --- a/src/docstrange/types/extract_stream_response.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import TypeAlias - -__all__ = ["ExtractStreamResponse"] - -ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 3208694..1f032ea 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -35,3 +35,4 @@ class ExtractSyncParams(TypedDict, total=False): """JSON extraction options""" prompt_mode: Literal["append", "replace"] + """append or replace""" diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py deleted file mode 100644 index 0b67e0f..0000000 --- a/tests/api_resources/extract/test_results.py +++ /dev/null @@ -1,205 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse -from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestResults: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_retrieve(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_retrieve(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_retrieve(self, client: Docstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list(self, client: Docstrange) -> None: - result = client.extract.results.list() - assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list_with_all_params(self, client: Docstrange) -> None: - result = client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_list(self, client: Docstrange) -> None: - response = client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = response.parse() - assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_list(self, client: Docstrange) -> None: - with client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = response.parse() - assert_matches_type(SyncPageNumberPagination[object], result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncResults: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.retrieve( - record_id="record_id", - include_content=True, - ) - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.retrieve( - record_id="record_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.retrieve( - record_id="record_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(ExtractResponse, result, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): - await async_client.extract.results.with_raw_response.retrieve( - record_id="", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list() - assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: - result = await async_client.extract.results.list( - page=1, - page_size=1, - sort_by="created_at", - sort_order="asc", - ) - assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.results.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.results.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - result = await response.parse() - assert_matches_type(AsyncPageNumberPagination[object], result, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py deleted file mode 100644 index 84bfced..0000000 --- a/tests/api_resources/test_chat.py +++ /dev/null @@ -1,171 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from docstrange import Docstrange, AsyncDocstrange -from tests.utils import assert_matches_type - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestChat: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: - chat = client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_create_completion(self, client: Docstrange) -> None: - response = client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_create_completion(self, client: Docstrange) -> None: - with client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncChat: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: - chat = await async_client.chat.create_completion( - messages=[ - { - "content": [ - { - "file_url": {"url": "url"}, - "image_url": {"url": "url"}, - "text": "text", - "type": "type", - } - ], - "role": "role", - } - ], - model="model", - stream=True, - ) - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: - response = await async_client.chat.with_raw_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: - async with async_client.chat.with_streaming_response.create_completion( - messages=[ - { - "content": [{}], - "role": "role", - } - ], - model="model", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chat = await response.parse() - assert_matches_type(object, chat, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index d40e1fa..63539df 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse, BatchClassifyResponse +from docstrange.types import ClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,43 +17,6 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - classify = client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -97,43 +60,6 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - classify = await async_client.classify.batch( - categories="categories", - files=[b"raw file contents"], - ) - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.classify.with_raw_response.batch( - categories="categories", - files=[b"raw file contents"], - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.classify.with_streaming_response.batch( - categories="categories", - files=[b"raw file contents"], - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - classify = await response.parse() - assert_matches_type(BatchClassifyResponse, classify, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index bd13f30..d911970 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,10 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ( - ExtractResponse, - BatchExtractResponse, -) +from docstrange.types import ExtractResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,163 +17,6 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_async_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.async_( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_async(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_async(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_batch_with_all_params(self, client: Docstrange) -> None: - extract = client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_batch(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_batch(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="markdown", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_method_stream_with_all_params(self, client: Docstrange) -> None: - extract_stream = client.extract.stream( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - extract_stream.response.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_raw_response_stream(self, client: Docstrange) -> None: - response = client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = response.parse() - stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - def test_streaming_response_stream(self, client: Docstrange) -> None: - with client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = response.parse() - stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: @@ -236,163 +76,6 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="markdown", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.async_( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.async_( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.async_( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(ExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract = await async_client.extract.batch( - files=[b"raw file contents"], - output_format="markdown", - csv_options="csv_options", - custom_instructions="custom_instructions", - include_metadata="include_metadata", - json_options="json_options", - prompt_mode="append", - ) - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.batch( - files=[b"raw file contents"], - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - extract = await response.parse() - assert_matches_type(BatchExtractResponse, extract, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="markdown", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: - extract_stream = await async_client.extract.stream( - file=b"raw file contents", - output_format="markdown", - csv_options="", - custom_instructions="", - enable_streaming=True, - file_base64="", - file_url="", - include_metadata="", - json_options="", - prompt_mode="append", - ) - await extract_stream.response.aclose() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: - response = await async_client.extract.with_raw_response.stream( - file=b"raw file contents", - output_format="markdown", - ) - - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - stream = await response.parse() - await stream.close() - - @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") - @parametrize - async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: - async with async_client.extract.with_streaming_response.stream( - file=b"raw file contents", - output_format="markdown", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - stream = await response.parse() - await stream.close() - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: From b03ae9b0d7d5940df2aaff8b4cfa22c163a31b6b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:32:27 +0000 Subject: [PATCH 23/26] feat(api): api update --- .stats.yml | 8 +- README.md | 71 +- api.md | 45 +- src/docstrange/_client.py | 39 +- src/docstrange/resources/__init__.py | 14 + src/docstrange/resources/chat.py | 218 ++++++ src/docstrange/resources/classify.py | 179 ++++- src/docstrange/resources/extract/__init__.py | 14 + src/docstrange/resources/extract/extract.py | 670 ++++++++++++++++-- src/docstrange/resources/extract/results.py | 294 ++++++++ src/docstrange/types/__init__.py | 13 + .../types/batch_classify_response.py | 24 + .../types/batch_extract_response.py | 24 + .../types/chat_create_completion_params.py | 45 ++ src/docstrange/types/classify_batch_params.py | 17 + src/docstrange/types/classify_response.py | 5 + src/docstrange/types/classify_sync_params.py | 7 +- src/docstrange/types/extract/__init__.py | 5 + .../types/extract/extraction_list_response.py | 17 + .../types/extract/pagination_info.py | 21 + .../types/extract/result_list_params.py | 17 + .../types/extract/result_retrieve_params.py | 12 + src/docstrange/types/extract_async_params.py | 45 ++ src/docstrange/types/extract_batch_params.py | 27 + src/docstrange/types/extract_response.py | 25 +- src/docstrange/types/extract_stream_params.py | 47 ++ .../types/extract_stream_response.py | 7 + src/docstrange/types/extract_sync_params.py | 25 +- .../types/extraction_format_result.py | 15 + src/docstrange/types/extraction_metadata.py | 15 + src/docstrange/types/extraction_result.py | 22 + .../types/file_classification_result.py | 24 + src/docstrange/types/page_classification.py | 27 + tests/api_resources/extract/test_results.py | 205 ++++++ tests/api_resources/test_chat.py | 183 +++++ tests/api_resources/test_classify.py | 88 ++- tests/api_resources/test_extract.py | 317 ++++++++- tests/test_client.py | 24 +- 38 files changed, 2728 insertions(+), 127 deletions(-) create mode 100644 src/docstrange/resources/chat.py create mode 100644 src/docstrange/resources/extract/results.py create mode 100644 src/docstrange/types/batch_classify_response.py create mode 100644 src/docstrange/types/batch_extract_response.py create mode 100644 src/docstrange/types/chat_create_completion_params.py create mode 100644 src/docstrange/types/classify_batch_params.py create mode 100644 src/docstrange/types/extract/extraction_list_response.py create mode 100644 src/docstrange/types/extract/pagination_info.py create mode 100644 src/docstrange/types/extract/result_list_params.py create mode 100644 src/docstrange/types/extract/result_retrieve_params.py create mode 100644 src/docstrange/types/extract_async_params.py create mode 100644 src/docstrange/types/extract_batch_params.py create mode 100644 src/docstrange/types/extract_stream_params.py create mode 100644 src/docstrange/types/extract_stream_response.py create mode 100644 src/docstrange/types/extraction_format_result.py create mode 100644 src/docstrange/types/extraction_metadata.py create mode 100644 src/docstrange/types/extraction_result.py create mode 100644 src/docstrange/types/file_classification_result.py create mode 100644 src/docstrange/types/page_classification.py create mode 100644 tests/api_resources/extract/test_results.py create mode 100644 tests/api_resources/test_chat.py diff --git a/.stats.yml b/.stats.yml index f839396..6864372 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-75dd621006675868b0701738226a045f2e9d7211242fece922f06478bef66738.yml -openapi_spec_hash: 47cb78aa2caa018fe58742fc19b0a49c -config_hash: aed0d6cc8b4cffa1f02021baec0a6da3 +configured_endpoints: 9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml +openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 +config_hash: 1804754b97b5ccf95ad31183c74527bc diff --git a/README.md b/README.md index 74c6d8b..917f116 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ client = Docstrange( ) extract_response = client.extract.sync( - file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -60,7 +59,6 @@ client = AsyncDocstrange( async def main() -> None: extract_response = await client.extract.sync( - file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -97,7 +95,6 @@ async def main() -> None: http_client=DefaultAioHttpClient(), ) as client: extract_response = await client.extract.sync( - file=b"raw file contents", output_format="markdown", ) print(extract_response.record_id) @@ -115,6 +112,67 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Docstrange API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from docstrange import Docstrange + +client = Docstrange() + +all_results = [] +# Automatically fetches more pages as needed. +for result in client.extract.results.list(): + # Do something with result here + all_results.append(result) +print(all_results) +``` + +Or, asynchronously: + +```python +import asyncio +from docstrange import AsyncDocstrange + +client = AsyncDocstrange() + + +async def main() -> None: + all_results = [] + # Iterate through items across all pages, issuing requests as needed. + async for result in client.extract.results.list(): + all_results.append(result) + print(all_results) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.extract.results.list() +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.results)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.extract.results.list() +for result in first_page.results: + print(result.record_id) + +# Remove `await` for non-async usage. +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. @@ -126,8 +184,8 @@ from docstrange import Docstrange client = Docstrange() client.extract.sync( - file=Path("/path/to/file"), output_format="markdown", + file=Path("/path/to/file"), ) ``` @@ -150,7 +208,6 @@ client = Docstrange() try: client.extract.sync( - file=b"raw file contents", output_format="markdown", ) except docstrange.APIConnectionError as e: @@ -196,7 +253,6 @@ client = Docstrange( # Or, configure per-request: client.with_options(max_retries=5).extract.sync( - file=b"raw file contents", output_format="markdown", ) ``` @@ -222,7 +278,6 @@ client = Docstrange( # Override per-request: client.with_options(timeout=5.0).extract.sync( - file=b"raw file contents", output_format="markdown", ) ``` @@ -266,7 +321,6 @@ from docstrange import Docstrange client = Docstrange() response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", ) print(response.headers.get('X-My-Header')) @@ -287,7 +341,6 @@ To stream the response body, use `.with_streaming_response` instead, which requi ```python with client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 96e2318..df1bce4 100644 --- a/api.md +++ b/api.md @@ -3,21 +3,62 @@ Types: ```python -from docstrange.types import ExtractRequestBody, ExtractResponse +from docstrange.types import ( + BatchExtractResponse, + ExtractResponse, + ExtractionFormatResult, + ExtractionMetadata, + ExtractionResult, + ExtractStreamResponse, +) ``` Methods: +- client.extract.async\_(\*\*params) -> ExtractResponse +- client.extract.batch(\*\*params) -> BatchExtractResponse +- client.extract.stream(\*\*params) -> str - client.extract.sync(\*\*params) -> ExtractResponse +## Results + +Types: + +```python +from docstrange.types.extract import ExtractionListResponse, PaginationInfo +``` + +Methods: + +- client.extract.results.retrieve(record_id, \*\*params) -> ExtractResponse +- client.extract.results.list(\*\*params) -> SyncPageNumberPagination[ExtractResponse] + # Classify Types: ```python -from docstrange.types import ClassifyRequestBody, ClassifyResponse +from docstrange.types import ( + BatchClassifyResponse, + ClassifyResponse, + FileClassificationResult, + PageClassification, +) ``` Methods: +- client.classify.batch(\*\*params) -> BatchClassifyResponse - client.classify.sync(\*\*params) -> ClassifyResponse + +# Chat + +Types: + +```python +from docstrange.types import ChatCompletionsRequest +``` + +Methods: + +- client.chat.create_completion(\*\*params) -> object diff --git a/src/docstrange/_client.py b/src/docstrange/_client.py index 271a014..4c5bcc4 100644 --- a/src/docstrange/_client.py +++ b/src/docstrange/_client.py @@ -31,7 +31,8 @@ ) if TYPE_CHECKING: - from .resources import extract, classify + from .resources import chat, extract, classify + from .resources.chat import ChatResource, AsyncChatResource from .resources.classify import ClassifyResource, AsyncClassifyResource from .resources.extract.extract import ExtractResource, AsyncExtractResource @@ -114,6 +115,12 @@ def classify(self) -> ClassifyResource: return ClassifyResource(self) + @cached_property + def chat(self) -> ChatResource: + from .resources.chat import ChatResource + + return ChatResource(self) + @cached_property def with_raw_response(self) -> DocstrangeWithRawResponse: return DocstrangeWithRawResponse(self) @@ -294,6 +301,12 @@ def classify(self) -> AsyncClassifyResource: return AsyncClassifyResource(self) + @cached_property + def chat(self) -> AsyncChatResource: + from .resources.chat import AsyncChatResource + + return AsyncChatResource(self) + @cached_property def with_raw_response(self) -> AsyncDocstrangeWithRawResponse: return AsyncDocstrangeWithRawResponse(self) @@ -425,6 +438,12 @@ def classify(self) -> classify.ClassifyResourceWithRawResponse: return ClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithRawResponse: + from .resources.chat import ChatResourceWithRawResponse + + return ChatResourceWithRawResponse(self._client.chat) + class AsyncDocstrangeWithRawResponse: _client: AsyncDocstrange @@ -444,6 +463,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithRawResponse: return AsyncClassifyResourceWithRawResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithRawResponse: + from .resources.chat import AsyncChatResourceWithRawResponse + + return AsyncChatResourceWithRawResponse(self._client.chat) + class DocstrangeWithStreamedResponse: _client: Docstrange @@ -463,6 +488,12 @@ def classify(self) -> classify.ClassifyResourceWithStreamingResponse: return ClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.ChatResourceWithStreamingResponse: + from .resources.chat import ChatResourceWithStreamingResponse + + return ChatResourceWithStreamingResponse(self._client.chat) + class AsyncDocstrangeWithStreamedResponse: _client: AsyncDocstrange @@ -482,6 +513,12 @@ def classify(self) -> classify.AsyncClassifyResourceWithStreamingResponse: return AsyncClassifyResourceWithStreamingResponse(self._client.classify) + @cached_property + def chat(self) -> chat.AsyncChatResourceWithStreamingResponse: + from .resources.chat import AsyncChatResourceWithStreamingResponse + + return AsyncChatResourceWithStreamingResponse(self._client.chat) + Client = Docstrange diff --git a/src/docstrange/resources/__init__.py b/src/docstrange/resources/__init__.py index 6d7ff55..a3172dd 100644 --- a/src/docstrange/resources/__init__.py +++ b/src/docstrange/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .chat import ( + ChatResource, + AsyncChatResource, + ChatResourceWithRawResponse, + AsyncChatResourceWithRawResponse, + ChatResourceWithStreamingResponse, + AsyncChatResourceWithStreamingResponse, +) from .extract import ( ExtractResource, AsyncExtractResource, @@ -30,4 +38,10 @@ "AsyncClassifyResourceWithRawResponse", "ClassifyResourceWithStreamingResponse", "AsyncClassifyResourceWithStreamingResponse", + "ChatResource", + "AsyncChatResource", + "ChatResourceWithRawResponse", + "AsyncChatResourceWithRawResponse", + "ChatResourceWithStreamingResponse", + "AsyncChatResourceWithStreamingResponse", ] diff --git a/src/docstrange/resources/chat.py b/src/docstrange/resources/chat.py new file mode 100644 index 0000000..0a892c1 --- /dev/null +++ b/src/docstrange/resources/chat.py @@ -0,0 +1,218 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..types import chat_create_completion_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options + +__all__ = ["ChatResource", "AsyncChatResource"] + + +class ChatResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ChatResourceWithStreamingResponse(self) + + def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """OpenAI-compatible chat completions endpoint. + + Supports text messages and + file/image inputs via `image_url` or `file_url` parts. + + **Limits and behavior:** + + - The backend enforces a model context limit of ~20,000 tokens (text + + multimodal tokens). + - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts + and embedded as data URLs. + - Inline data URLs are limited to ~64MB decoded size per item. + + For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + + Args: + messages: Chat messages array + + model: Model identifier (e.g., nanonets/Nanonets-OCR-s) + + stream: Enable SSE streaming + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v1/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncChatResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChatResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncChatResourceWithStreamingResponse(self) + + async def create_completion( + self, + *, + messages: Iterable[chat_create_completion_params.Message], + model: str, + stream: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """OpenAI-compatible chat completions endpoint. + + Supports text messages and + file/image inputs via `image_url` or `file_url` parts. + + **Limits and behavior:** + + - The backend enforces a model context limit of ~20,000 tokens (text + + multimodal tokens). + - `file_url` inputs (e.g., PDFs) are expanded into per-page `image_url` parts + and embedded as data URLs. + - Inline data URLs are limited to ~64MB decoded size per item. + + For large or multi-page documents, prefer `/api/v1/extract/*` endpoints. + + Args: + messages: Chat messages array + + model: Model identifier (e.g., nanonets/Nanonets-OCR-s) + + stream: Enable SSE streaming + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v1/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "stream": stream, + }, + chat_create_completion_params.ChatCreateCompletionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ChatResourceWithRawResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_raw_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithRawResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_raw_response_wrapper( + chat.create_completion, + ) + + +class ChatResourceWithStreamingResponse: + def __init__(self, chat: ChatResource) -> None: + self._chat = chat + + self.create_completion = to_streamed_response_wrapper( + chat.create_completion, + ) + + +class AsyncChatResourceWithStreamingResponse: + def __init__(self, chat: AsyncChatResource) -> None: + self._chat = chat + + self.create_completion = async_to_streamed_response_wrapper( + chat.create_completion, + ) diff --git a/src/docstrange/resources/classify.py b/src/docstrange/resources/classify.py index 382aa89..797bf65 100644 --- a/src/docstrange/resources/classify.py +++ b/src/docstrange/resources/classify.py @@ -2,11 +2,13 @@ from __future__ import annotations +from typing import Mapping, cast + import httpx -from ..types import classify_sync_params -from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from .._utils import maybe_transform, async_maybe_transform +from ..types import classify_sync_params, classify_batch_params +from .._types import Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,6 +19,7 @@ ) from .._base_client import make_request_options from ..types.classify_response import ClassifyResponse +from ..types.batch_classify_response import BatchClassifyResponse __all__ = ["ClassifyResource", "AsyncClassifyResource"] @@ -41,6 +44,58 @@ def with_streaming_response(self) -> ClassifyResourceWithStreamingResponse: """ return ClassifyResourceWithStreamingResponse(self) + def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50 files). + + Files are queued for background processing. Use + `GET /api/v1/extract/results/{record_id}` to poll results. + + Args: + categories: JSON array of category objects (max 50 categories) + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "categories": categories, + "files": files, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/classify/batch", + body=maybe_transform(body, classify_batch_params.ClassifyBatchParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + def sync( self, *, @@ -54,12 +109,17 @@ def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document. + Classify a single document into predefined categories synchronously. + + Each page is classified individually with category, confidence score (0-100), + and reasoning. Args: - categories: JSON array of category objects + categories: + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] - file: File to classify + file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) extra_headers: Send extra headers @@ -69,19 +129,21 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/api/v1/classify/sync", - body=maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -109,6 +171,58 @@ def with_streaming_response(self) -> AsyncClassifyResourceWithStreamingResponse: """ return AsyncClassifyResourceWithStreamingResponse(self) + async def batch( + self, + *, + categories: str, + files: SequenceNotStr[FileTypes], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchClassifyResponse: + """ + Classify multiple documents asynchronously (max 50 files). + + Files are queued for background processing. Use + `GET /api/v1/extract/results/{record_id}` to poll results. + + Args: + categories: JSON array of category objects (max 50 categories) + + files: Files to classify (max 50) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "categories": categories, + "files": files, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/classify/batch", + body=await async_maybe_transform(body, classify_batch_params.ClassifyBatchParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchClassifyResponse, + ) + async def sync( self, *, @@ -122,12 +236,17 @@ async def sync( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClassifyResponse: """ - Classify a single document. + Classify a single document into predefined categories synchronously. + + Each page is classified individually with category, confidence score (0-100), + and reasoning. Args: - categories: JSON array of category objects + categories: + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] - file: File to classify + file: File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP) extra_headers: Send extra headers @@ -137,19 +256,21 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ + body = deepcopy_minimal( + { + "categories": categories, + "file": file, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/api/v1/classify/sync", - body=await async_maybe_transform( - { - "categories": categories, - "file": file, - }, - classify_sync_params.ClassifySyncParams, - ), + body=await async_maybe_transform(body, classify_sync_params.ClassifySyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -161,6 +282,9 @@ class ClassifyResourceWithRawResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_raw_response_wrapper( + classify.batch, + ) self.sync = to_raw_response_wrapper( classify.sync, ) @@ -170,6 +294,9 @@ class AsyncClassifyResourceWithRawResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_raw_response_wrapper( + classify.batch, + ) self.sync = async_to_raw_response_wrapper( classify.sync, ) @@ -179,6 +306,9 @@ class ClassifyResourceWithStreamingResponse: def __init__(self, classify: ClassifyResource) -> None: self._classify = classify + self.batch = to_streamed_response_wrapper( + classify.batch, + ) self.sync = to_streamed_response_wrapper( classify.sync, ) @@ -188,6 +318,9 @@ class AsyncClassifyResourceWithStreamingResponse: def __init__(self, classify: AsyncClassifyResource) -> None: self._classify = classify + self.batch = async_to_streamed_response_wrapper( + classify.batch, + ) self.sync = async_to_streamed_response_wrapper( classify.sync, ) diff --git a/src/docstrange/resources/extract/__init__.py b/src/docstrange/resources/extract/__init__.py index a0fee27..22cd257 100644 --- a/src/docstrange/resources/extract/__init__.py +++ b/src/docstrange/resources/extract/__init__.py @@ -8,8 +8,22 @@ ExtractResourceWithStreamingResponse, AsyncExtractResourceWithStreamingResponse, ) +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) __all__ = [ + "ResultsResource", + "AsyncResultsResource", + "ResultsResourceWithRawResponse", + "AsyncResultsResourceWithRawResponse", + "ResultsResourceWithStreamingResponse", + "AsyncResultsResourceWithStreamingResponse", "ExtractResource", "AsyncExtractResource", "ExtractResourceWithRawResponse", diff --git a/src/docstrange/resources/extract/extract.py b/src/docstrange/resources/extract/extract.py index f5b48d9..5a0f0fa 100644 --- a/src/docstrange/resources/extract/extract.py +++ b/src/docstrange/resources/extract/extract.py @@ -2,13 +2,32 @@ from __future__ import annotations +from typing import Mapping, cast from typing_extensions import Literal import httpx -from ...types import extract_sync_params -from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ...types import extract_sync_params, extract_async_params, extract_batch_params, extract_stream_params +from .results import ( + ResultsResource, + AsyncResultsResource, + ResultsResourceWithRawResponse, + AsyncResultsResourceWithRawResponse, + ResultsResourceWithStreamingResponse, + AsyncResultsResourceWithStreamingResponse, +) +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -17,13 +36,20 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) +from ..._streaming import Stream, AsyncStream from ..._base_client import make_request_options from ...types.extract_response import ExtractResponse +from ...types.batch_extract_response import BatchExtractResponse +from ...types.extract_stream_response import ExtractStreamResponse __all__ = ["ExtractResource", "AsyncExtractResource"] class ExtractResource(SyncAPIResource): + @cached_property + def results(self) -> ResultsResource: + return ResultsResource(self._client) + @cached_property def with_raw_response(self) -> ExtractResourceWithRawResponse: """ @@ -43,13 +69,13 @@ def with_streaming_response(self) -> ExtractResourceWithStreamingResponse: """ return ExtractResourceWithStreamingResponse(self) - def sync( + def async_( self, *, - file: FileTypes, output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file: FileTypes | Omit = omit, file_base64: str | Omit = omit, file_url: str | Omit = omit, include_metadata: str | Omit = omit, @@ -62,27 +88,187 @@ def sync( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. + """Queue a document for background processing. + + Returns a `record_id` to poll + results via `GET /api/v1/extract/results/{record_id}`. + + Recommended for large documents (>50 pages). Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file: File to upload (PDF, Word, Excel, PowerPoint, images) - output_format: Output format(s): markdown, html, json, csv. + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/async", + body=maybe_transform(body, extract_async_params.ExtractAsyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """Process multiple files asynchronously (max 50 files). + + All files share the same + extraction options. + + Args: + files: Files to process (max 50) + + output_format: Output format(s) for all files + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/batch", + body=maybe_transform(body, extract_batch_params.ExtractBatchParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + def stream( + self, + *, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file: FileTypes | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[ExtractStreamResponse]: + """ + Stream extraction results via Server-Sent Events (SSE) for real-time content + delivery. + + **Event Types:** + + - `content`: Incremental content chunks (streaming mode) + - `complete`: Full content at once (batch mode) + - `done`: Final event with record_id and processing_time + - `error`: Error information + - `async_queued`: Large files queued for async processing + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for + multiple. csv_options: CSV extraction options custom_instructions: Custom extraction instructions + enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch + mode. + + file: File to upload (PDF, Word, Excel, PowerPoint, images) + file_base64: Base64-encoded file content file_url: URL to download file from - include_metadata: Comma-separated metadata types + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options - prompt_mode: append or replace + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions extra_headers: Send extra headers @@ -92,26 +278,115 @@ def sync( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - "/api/v1/extract/sync", - body=maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, + "/api/v1/extract/stream", + body=maybe_transform(body, extract_stream_params.ExtractStreamParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), + cast_to=str, + stream=True, + stream_cls=Stream[ExtractStreamResponse], + ) + + def sync( + self, + *, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file: FileTypes | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Process a document and return extracted content immediately. + + (Works for files + with 5 pages or less.) + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/extract/sync", + body=maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -120,6 +395,10 @@ def sync( class AsyncExtractResource(AsyncAPIResource): + @cached_property + def results(self) -> AsyncResultsResource: + return AsyncResultsResource(self._client) + @cached_property def with_raw_response(self) -> AsyncExtractResourceWithRawResponse: """ @@ -139,13 +418,13 @@ def with_streaming_response(self) -> AsyncExtractResourceWithStreamingResponse: """ return AsyncExtractResourceWithStreamingResponse(self) - async def sync( + async def async_( self, *, - file: FileTypes, output_format: str, csv_options: str | Omit = omit, custom_instructions: str | Omit = omit, + file: FileTypes | Omit = omit, file_base64: str | Omit = omit, file_url: str | Omit = omit, include_metadata: str | Omit = omit, @@ -158,27 +437,187 @@ async def sync( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ExtractResponse: - """ - Process a document and return extracted content immediately. + """Queue a document for background processing. + + Returns a `record_id` to poll + results via `GET /api/v1/extract/results/{record_id}`. + + Recommended for large documents (>50 pages). Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + file: File to upload (PDF, Word, Excel, PowerPoint, images) - output_format: Output format(s): markdown, html, json, csv. + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/async", + body=await async_maybe_transform(body, extract_async_params.ExtractAsyncParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtractResponse, + ) + + async def batch( + self, + *, + files: SequenceNotStr[FileTypes], + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BatchExtractResponse: + """Process multiple files asynchronously (max 50 files). + + All files share the same + extraction options. + + Args: + files: Files to process (max 50) + + output_format: Output format(s) for all files + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "files": files, + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/batch", + body=await async_maybe_transform(body, extract_batch_params.ExtractBatchParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BatchExtractResponse, + ) + + async def stream( + self, + *, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + enable_streaming: bool | Omit = omit, + file: FileTypes | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ExtractStreamResponse]: + """ + Stream extraction results via Server-Sent Events (SSE) for real-time content + delivery. + + **Event Types:** + + - `content`: Incremental content chunks (streaming mode) + - `complete`: Full content at once (batch mode) + - `done`: Final event with record_id and processing_time + - `error`: Error information + - `async_queued`: Large files queued for async processing + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for + multiple. csv_options: CSV extraction options custom_instructions: Custom extraction instructions + enable_streaming: Enable real-time streaming. If false, returns complete content via SSE batch + mode. + + file: File to upload (PDF, Word, Excel, PowerPoint, images) + file_base64: Base64-encoded file content file_url: URL to download file from - include_metadata: Comma-separated metadata types + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` json_options: JSON extraction options - prompt_mode: append or replace + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions extra_headers: Send extra headers @@ -188,26 +627,115 @@ async def sync( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "enable_streaming": enable_streaming, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - "/api/v1/extract/sync", - body=await async_maybe_transform( - { - "file": file, - "output_format": output_format, - "csv_options": csv_options, - "custom_instructions": custom_instructions, - "file_base64": file_base64, - "file_url": file_url, - "include_metadata": include_metadata, - "json_options": json_options, - "prompt_mode": prompt_mode, - }, - extract_sync_params.ExtractSyncParams, + "/api/v1/extract/stream", + body=await async_maybe_transform(body, extract_stream_params.ExtractStreamParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), + cast_to=str, + stream=True, + stream_cls=AsyncStream[ExtractStreamResponse], + ) + + async def sync( + self, + *, + output_format: str, + csv_options: str | Omit = omit, + custom_instructions: str | Omit = omit, + file: FileTypes | Omit = omit, + file_base64: str | Omit = omit, + file_url: str | Omit = omit, + include_metadata: str | Omit = omit, + json_options: str | Omit = omit, + prompt_mode: Literal["append", "replace"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """Process a document and return extracted content immediately. + + (Works for files + with 5 pages or less.) + + Provide exactly one of: `file`, `file_url`, or `file_base64`. + + Args: + output_format: Output format(s): `markdown`, `html`, `json`, `csv`. Comma-separate for multiple + (e.g., `markdown,json`). + + csv_options: CSV extraction options (e.g., `table`) + + custom_instructions: Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`) + + file: File to upload (PDF, Word, Excel, PowerPoint, images) + + file_base64: Base64-encoded file content + + file_url: URL to download file from + + include_metadata: Comma-separated metadata: `bounding_boxes`, `confidence_score` + + json_options: JSON extraction options. Values: `hierarchy_output`, `table-of-contents`, field + list `["field1", "field2"]`, or JSON schema `{...}` + + prompt_mode: `append`: add to base prompt, `replace`: use only custom instructions + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "output_format": output_format, + "csv_options": csv_options, + "custom_instructions": custom_instructions, + "file": file, + "file_base64": file_base64, + "file_url": file_url, + "include_metadata": include_metadata, + "json_options": json_options, + "prompt_mode": prompt_mode, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/extract/sync", + body=await async_maybe_transform(body, extract_sync_params.ExtractSyncParams), + files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -219,33 +747,85 @@ class ExtractResourceWithRawResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract + self.async_ = to_raw_response_wrapper( + extract.async_, + ) + self.batch = to_raw_response_wrapper( + extract.batch, + ) + self.stream = to_raw_response_wrapper( + extract.stream, + ) self.sync = to_raw_response_wrapper( extract.sync, ) + @cached_property + def results(self) -> ResultsResourceWithRawResponse: + return ResultsResourceWithRawResponse(self._extract.results) + class AsyncExtractResourceWithRawResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract + self.async_ = async_to_raw_response_wrapper( + extract.async_, + ) + self.batch = async_to_raw_response_wrapper( + extract.batch, + ) + self.stream = async_to_raw_response_wrapper( + extract.stream, + ) self.sync = async_to_raw_response_wrapper( extract.sync, ) + @cached_property + def results(self) -> AsyncResultsResourceWithRawResponse: + return AsyncResultsResourceWithRawResponse(self._extract.results) + class ExtractResourceWithStreamingResponse: def __init__(self, extract: ExtractResource) -> None: self._extract = extract + self.async_ = to_streamed_response_wrapper( + extract.async_, + ) + self.batch = to_streamed_response_wrapper( + extract.batch, + ) + self.stream = to_streamed_response_wrapper( + extract.stream, + ) self.sync = to_streamed_response_wrapper( extract.sync, ) + @cached_property + def results(self) -> ResultsResourceWithStreamingResponse: + return ResultsResourceWithStreamingResponse(self._extract.results) + class AsyncExtractResourceWithStreamingResponse: def __init__(self, extract: AsyncExtractResource) -> None: self._extract = extract + self.async_ = async_to_streamed_response_wrapper( + extract.async_, + ) + self.batch = async_to_streamed_response_wrapper( + extract.batch, + ) + self.stream = async_to_streamed_response_wrapper( + extract.stream, + ) self.sync = async_to_streamed_response_wrapper( extract.sync, ) + + @cached_property + def results(self) -> AsyncResultsResourceWithStreamingResponse: + return AsyncResultsResourceWithStreamingResponse(self._extract.results) diff --git a/src/docstrange/resources/extract/results.py b/src/docstrange/resources/extract/results.py new file mode 100644 index 0000000..1533ac2 --- /dev/null +++ b/src/docstrange/resources/extract/results.py @@ -0,0 +1,294 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination +from ..._base_client import AsyncPaginator, make_request_options +from ...types.extract import result_list_params, result_retrieve_params +from ...types.extract_response import ExtractResponse + +__all__ = ["ResultsResource", "AsyncResultsResource"] + + +class ResultsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return ResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return ResultsResourceWithStreamingResponse(self) + + def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + include_content: Include full extracted content (default: true) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPageNumberPagination[ExtractResponse]: + """ + List all extraction jobs for the authenticated user (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=SyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class AsyncResultsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncResultsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/NanoNets/docstrange-python#accessing-raw-response-data-eg-headers + """ + return AsyncResultsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResultsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/NanoNets/docstrange-python#with_streaming_response + """ + return AsyncResultsResourceWithStreamingResponse(self) + + async def retrieve( + self, + record_id: str, + *, + include_content: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExtractResponse: + """ + Retrieve status and result of an extraction job. + + Args: + include_content: Include full extracted content (default: true) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not record_id: + raise ValueError(f"Expected a non-empty value for `record_id` but received {record_id!r}") + return await self._get( + f"/api/v1/extract/results/{record_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_content": include_content}, result_retrieve_params.ResultRetrieveParams + ), + ), + cast_to=ExtractResponse, + ) + + def list( + self, + *, + page: int | Omit = omit, + page_size: int | Omit = omit, + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + | Omit = omit, + sort_order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ExtractResponse, AsyncPageNumberPagination[ExtractResponse]]: + """ + List all extraction jobs for the authenticated user (paginated). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/extract/results", + page=AsyncPageNumberPagination[ExtractResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + "sort_by": sort_by, + "sort_order": sort_order, + }, + result_list_params.ResultListParams, + ), + ), + model=ExtractResponse, + ) + + +class ResultsResourceWithRawResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_raw_response_wrapper( + results.retrieve, + ) + self.list = to_raw_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithRawResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_raw_response_wrapper( + results.retrieve, + ) + self.list = async_to_raw_response_wrapper( + results.list, + ) + + +class ResultsResourceWithStreamingResponse: + def __init__(self, results: ResultsResource) -> None: + self._results = results + + self.retrieve = to_streamed_response_wrapper( + results.retrieve, + ) + self.list = to_streamed_response_wrapper( + results.list, + ) + + +class AsyncResultsResourceWithStreamingResponse: + def __init__(self, results: AsyncResultsResource) -> None: + self._results = results + + self.retrieve = async_to_streamed_response_wrapper( + results.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + results.list, + ) diff --git a/src/docstrange/types/__init__.py b/src/docstrange/types/__init__.py index fac3955..a18a10a 100644 --- a/src/docstrange/types/__init__.py +++ b/src/docstrange/types/__init__.py @@ -4,5 +4,18 @@ from .extract_response import ExtractResponse as ExtractResponse from .classify_response import ClassifyResponse as ClassifyResponse +from .extraction_result import ExtractionResult as ExtractionResult from .extract_sync_params import ExtractSyncParams as ExtractSyncParams +from .extraction_metadata import ExtractionMetadata as ExtractionMetadata +from .page_classification import PageClassification as PageClassification from .classify_sync_params import ClassifySyncParams as ClassifySyncParams +from .extract_async_params import ExtractAsyncParams as ExtractAsyncParams +from .extract_batch_params import ExtractBatchParams as ExtractBatchParams +from .classify_batch_params import ClassifyBatchParams as ClassifyBatchParams +from .extract_stream_params import ExtractStreamParams as ExtractStreamParams +from .batch_extract_response import BatchExtractResponse as BatchExtractResponse +from .batch_classify_response import BatchClassifyResponse as BatchClassifyResponse +from .extract_stream_response import ExtractStreamResponse as ExtractStreamResponse +from .extraction_format_result import ExtractionFormatResult as ExtractionFormatResult +from .file_classification_result import FileClassificationResult as FileClassificationResult +from .chat_create_completion_params import ChatCreateCompletionParams as ChatCreateCompletionParams diff --git a/src/docstrange/types/batch_classify_response.py b/src/docstrange/types/batch_classify_response.py new file mode 100644 index 0000000..a36e5a8 --- /dev/null +++ b/src/docstrange/types/batch_classify_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .file_classification_result import FileClassificationResult + +__all__ = ["BatchClassifyResponse"] + + +class BatchClassifyResponse(BaseModel): + batch_id: str + + failed_files: int + + message: str + + results: List[FileClassificationResult] + + success: bool + + successful_files: int + + total_files: int diff --git a/src/docstrange/types/batch_extract_response.py b/src/docstrange/types/batch_extract_response.py new file mode 100644 index 0000000..4e73773 --- /dev/null +++ b/src/docstrange/types/batch_extract_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .extract_response import ExtractResponse + +__all__ = ["BatchExtractResponse"] + + +class BatchExtractResponse(BaseModel): + accepted_files: int + + batch_id: str + + message: str + + records: List[ExtractResponse] + + rejected_files: int + + success: bool + + total_files: int diff --git a/src/docstrange/types/chat_create_completion_params.py b/src/docstrange/types/chat_create_completion_params.py new file mode 100644 index 0000000..b7cde5c --- /dev/null +++ b/src/docstrange/types/chat_create_completion_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCreateCompletionParams", "Message", "MessageContent", "MessageContentFileURL", "MessageContentImageURL"] + + +class ChatCreateCompletionParams(TypedDict, total=False): + messages: Required[Iterable[Message]] + """Chat messages array""" + + model: Required[str] + """Model identifier (e.g., nanonets/Nanonets-OCR-s)""" + + stream: bool + """Enable SSE streaming""" + + +class MessageContentFileURL(TypedDict, total=False): + url: str + + +class MessageContentImageURL(TypedDict, total=False): + url: str + + +class MessageContent(TypedDict, total=False): + file_url: MessageContentFileURL + + image_url: MessageContentImageURL + + text: str + + type: str + + +class Message(TypedDict, total=False): + content: Required[Iterable[MessageContent]] + """Message content as an array of parts (text or file/image URLs).""" + + role: Required[str] + """Message role (system, user, assistant)""" diff --git a/src/docstrange/types/classify_batch_params.py b/src/docstrange/types/classify_batch_params.py new file mode 100644 index 0000000..5cac9e3 --- /dev/null +++ b/src/docstrange/types/classify_batch_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ClassifyBatchParams"] + + +class ClassifyBatchParams(TypedDict, total=False): + categories: Required[str] + """JSON array of category objects (max 50 categories)""" + + files: Required[SequenceNotStr[FileTypes]] + """Files to classify (max 50)""" diff --git a/src/docstrange/types/classify_response.py b/src/docstrange/types/classify_response.py index 5bab3de..fdebbb0 100644 --- a/src/docstrange/types/classify_response.py +++ b/src/docstrange/types/classify_response.py @@ -1,6 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from .._models import BaseModel +from .file_classification_result import FileClassificationResult __all__ = ["ClassifyResponse"] @@ -9,3 +12,5 @@ class ClassifyResponse(BaseModel): message: str success: bool + + result: Optional[FileClassificationResult] = None diff --git a/src/docstrange/types/classify_sync_params.py b/src/docstrange/types/classify_sync_params.py index d0b3e16..772427d 100644 --- a/src/docstrange/types/classify_sync_params.py +++ b/src/docstrange/types/classify_sync_params.py @@ -11,7 +11,10 @@ class ClassifySyncParams(TypedDict, total=False): categories: Required[str] - """JSON array of category objects""" + """ + JSON array of category objects: [{"name": "Category Name", "description": + "Optional description"}] + """ file: Required[FileTypes] - """File to classify""" + """File to classify (PDF, PNG, JPG, JPEG, TIFF, BMP, WebP)""" diff --git a/src/docstrange/types/extract/__init__.py b/src/docstrange/types/extract/__init__.py index f8ee8b1..387a007 100644 --- a/src/docstrange/types/extract/__init__.py +++ b/src/docstrange/types/extract/__init__.py @@ -1,3 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations + +from .pagination_info import PaginationInfo as PaginationInfo +from .result_list_params import ResultListParams as ResultListParams +from .result_retrieve_params import ResultRetrieveParams as ResultRetrieveParams +from .extraction_list_response import ExtractionListResponse as ExtractionListResponse diff --git a/src/docstrange/types/extract/extraction_list_response.py b/src/docstrange/types/extract/extraction_list_response.py new file mode 100644 index 0000000..26a12e7 --- /dev/null +++ b/src/docstrange/types/extract/extraction_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .pagination_info import PaginationInfo +from ..extract_response import ExtractResponse + +__all__ = ["ExtractionListResponse"] + + +class ExtractionListResponse(BaseModel): + pagination: PaginationInfo + + results: List[ExtractResponse] + + success: bool diff --git a/src/docstrange/types/extract/pagination_info.py b/src/docstrange/types/extract/pagination_info.py new file mode 100644 index 0000000..7a678a9 --- /dev/null +++ b/src/docstrange/types/extract/pagination_info.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["PaginationInfo"] + + +class PaginationInfo(BaseModel): + has_next: Optional[bool] = None + + has_previous: Optional[bool] = None + + page: Optional[int] = None + + page_size: Optional[int] = None + + total_count: Optional[int] = None + + total_pages: Optional[int] = None diff --git a/src/docstrange/types/extract/result_list_params.py b/src/docstrange/types/extract/result_list_params.py new file mode 100644 index 0000000..7dfe38a --- /dev/null +++ b/src/docstrange/types/extract/result_list_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ResultListParams"] + + +class ResultListParams(TypedDict, total=False): + page: int + + page_size: int + + sort_by: Literal["created_at", "updated_at", "original_filename", "file_size", "processing_status"] + + sort_order: Literal["asc", "desc"] diff --git a/src/docstrange/types/extract/result_retrieve_params.py b/src/docstrange/types/extract/result_retrieve_params.py new file mode 100644 index 0000000..539b80d --- /dev/null +++ b/src/docstrange/types/extract/result_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ResultRetrieveParams"] + + +class ResultRetrieveParams(TypedDict, total=False): + include_content: bool + """Include full extracted content (default: true)""" diff --git a/src/docstrange/types/extract_async_params.py b/src/docstrange/types/extract_async_params.py new file mode 100644 index 0000000..d638b09 --- /dev/null +++ b/src/docstrange/types/extract_async_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractAsyncParams"] + + +class ExtractAsyncParams(TypedDict, total=False): + output_format: Required[str] + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple (e.g., `markdown,json`). + """ + + csv_options: str + """CSV extraction options (e.g., `table`)""" + + custom_instructions: str + """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + + file: FileTypes + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" + + json_options: str + """JSON extraction options. + + Values: `hierarchy_output`, `table-of-contents`, field list + `["field1", "field2"]`, or JSON schema `{...}` + """ + + prompt_mode: Literal["append", "replace"] + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_batch_params.py b/src/docstrange/types/extract_batch_params.py new file mode 100644 index 0000000..bdc7b29 --- /dev/null +++ b/src/docstrange/types/extract_batch_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ExtractBatchParams"] + + +class ExtractBatchParams(TypedDict, total=False): + files: Required[SequenceNotStr[FileTypes]] + """Files to process (max 50)""" + + output_format: Required[str] + """Output format(s) for all files""" + + csv_options: str + + custom_instructions: str + + include_metadata: str + + json_options: str + + prompt_mode: Literal["append", "replace"] diff --git a/src/docstrange/types/extract_response.py b/src/docstrange/types/extract_response.py index ba021e0..9d93ead 100644 --- a/src/docstrange/types/extract_response.py +++ b/src/docstrange/types/extract_response.py @@ -1,17 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime +from typing_extensions import Literal from .._models import BaseModel +from .extraction_result import ExtractionResult __all__ = ["ExtractResponse"] class ExtractResponse(BaseModel): + message: str + record_id: str + """Job ID for retrieving results""" - status: str + status: Literal["completed", "processing", "failed"] success: bool - message: Optional[str] = None + created_at: Optional[datetime] = None + + file_size: Optional[int] = None + """Size in bytes""" + + filename: Optional[str] = None + + output_format: Optional[str] = None + + pages_processed: Optional[int] = None + + processing_time: Optional[float] = None + """Time in seconds""" + + result: Optional[ExtractionResult] = None + """Results by format (only requested formats populated)""" diff --git a/src/docstrange/types/extract_stream_params.py b/src/docstrange/types/extract_stream_params.py new file mode 100644 index 0000000..2a8e9e3 --- /dev/null +++ b/src/docstrange/types/extract_stream_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ExtractStreamParams"] + + +class ExtractStreamParams(TypedDict, total=False): + output_format: Required[str] + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple. + """ + + csv_options: str + """CSV extraction options""" + + custom_instructions: str + """Custom extraction instructions""" + + enable_streaming: bool + """Enable real-time streaming. + + If false, returns complete content via SSE batch mode. + """ + + file: FileTypes + """File to upload (PDF, Word, Excel, PowerPoint, images)""" + + file_base64: str + """Base64-encoded file content""" + + file_url: str + """URL to download file from""" + + include_metadata: str + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" + + json_options: str + """JSON extraction options""" + + prompt_mode: Literal["append", "replace"] + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extract_stream_response.py b/src/docstrange/types/extract_stream_response.py new file mode 100644 index 0000000..333ff97 --- /dev/null +++ b/src/docstrange/types/extract_stream_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ExtractStreamResponse"] + +ExtractStreamResponse: TypeAlias = str diff --git a/src/docstrange/types/extract_sync_params.py b/src/docstrange/types/extract_sync_params.py index 1f032ea..e3c4bea 100644 --- a/src/docstrange/types/extract_sync_params.py +++ b/src/docstrange/types/extract_sync_params.py @@ -10,17 +10,20 @@ class ExtractSyncParams(TypedDict, total=False): - file: Required[FileTypes] - """File to upload (PDF, Word, Excel, PowerPoint, images)""" - output_format: Required[str] - """Output format(s): markdown, html, json, csv.""" + """Output format(s): `markdown`, `html`, `json`, `csv`. + + Comma-separate for multiple (e.g., `markdown,json`). + """ csv_options: str - """CSV extraction options""" + """CSV extraction options (e.g., `table`)""" custom_instructions: str - """Custom extraction instructions""" + """Custom extraction instructions (e.g., `Format dates as YYYY-MM-DD`)""" + + file: FileTypes + """File to upload (PDF, Word, Excel, PowerPoint, images)""" file_base64: str """Base64-encoded file content""" @@ -29,10 +32,14 @@ class ExtractSyncParams(TypedDict, total=False): """URL to download file from""" include_metadata: str - """Comma-separated metadata types""" + """Comma-separated metadata: `bounding_boxes`, `confidence_score`""" json_options: str - """JSON extraction options""" + """JSON extraction options. + + Values: `hierarchy_output`, `table-of-contents`, field list + `["field1", "field2"]`, or JSON schema `{...}` + """ prompt_mode: Literal["append", "replace"] - """append or replace""" + """`append`: add to base prompt, `replace`: use only custom instructions""" diff --git a/src/docstrange/types/extraction_format_result.py b/src/docstrange/types/extraction_format_result.py new file mode 100644 index 0000000..579500c --- /dev/null +++ b/src/docstrange/types/extraction_format_result.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional + +from .._models import BaseModel +from .extraction_metadata import ExtractionMetadata + +__all__ = ["ExtractionFormatResult"] + + +class ExtractionFormatResult(BaseModel): + content: Union[str, object] + """Extracted content""" + + metadata: Optional[ExtractionMetadata] = None diff --git a/src/docstrange/types/extraction_metadata.py b/src/docstrange/types/extraction_metadata.py new file mode 100644 index 0000000..9792ba0 --- /dev/null +++ b/src/docstrange/types/extraction_metadata.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExtractionMetadata"] + + +class ExtractionMetadata(BaseModel): + bounding_boxes: Optional[object] = None + """Element coordinates with page dimensions""" + + confidence_score: Optional[object] = None + """Confidence scores per field""" diff --git a/src/docstrange/types/extraction_result.py b/src/docstrange/types/extraction_result.py new file mode 100644 index 0000000..57b2928 --- /dev/null +++ b/src/docstrange/types/extraction_result.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .extraction_format_result import ExtractionFormatResult + +__all__ = ["ExtractionResult"] + + +class ExtractionResult(BaseModel): + """Results by format (only requested formats populated)""" + + csv: Optional[ExtractionFormatResult] = None + + html: Optional[ExtractionFormatResult] = None + + json_: Optional[ExtractionFormatResult] = FieldInfo(alias="json", default=None) + + markdown: Optional[ExtractionFormatResult] = None diff --git a/src/docstrange/types/file_classification_result.py b/src/docstrange/types/file_classification_result.py new file mode 100644 index 0000000..397d924 --- /dev/null +++ b/src/docstrange/types/file_classification_result.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .page_classification import PageClassification + +__all__ = ["FileClassificationResult"] + + +class FileClassificationResult(BaseModel): + filename: str + + pages: List[PageClassification] + + total_pages: int + + error: Optional[str] = None + + processing_time: Optional[float] = None + """Time in seconds""" + + record_id: Optional[str] = None + """Record ID for polling results""" diff --git a/src/docstrange/types/page_classification.py b/src/docstrange/types/page_classification.py new file mode 100644 index 0000000..2099524 --- /dev/null +++ b/src/docstrange/types/page_classification.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["PageClassification"] + + +class PageClassification(BaseModel): + category: str + """Matched category name or 'Other' if no match""" + + confidence: int + """Confidence score (0-100)""" + + page_number: int + + reasoning: str + """Explanation for the classification""" + + identified_category: Optional[str] = None + """ + Specific document type identified by the model (e.g., + 'Invoice_Amazon_Electronics'). Always provided - contains a more detailed + classification than the category. + """ diff --git a/tests/api_resources/extract/test_results.py b/tests/api_resources/extract/test_results.py new file mode 100644 index 0000000..ee1d5f5 --- /dev/null +++ b/tests/api_resources/extract/test_results.py @@ -0,0 +1,205 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type +from docstrange.types import ExtractResponse +from docstrange.pagination import SyncPageNumberPagination, AsyncPageNumberPagination + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResults: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: Docstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Docstrange) -> None: + result = client.extract.results.list() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Docstrange) -> None: + result = client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Docstrange) -> None: + response = client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Docstrange) -> None: + with client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = response.parse() + assert_matches_type(SyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncResults: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.retrieve( + record_id="record_id", + include_content=True, + ) + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.retrieve( + record_id="record_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.retrieve( + record_id="record_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(ExtractResponse, result, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncDocstrange) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `record_id` but received ''"): + await async_client.extract.results.with_raw_response.retrieve( + record_id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncDocstrange) -> None: + result = await async_client.extract.results.list( + page=1, + page_size=1, + sort_by="created_at", + sort_order="asc", + ) + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.results.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.results.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + result = await response.parse() + assert_matches_type(AsyncPageNumberPagination[ExtractResponse], result, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py new file mode 100644 index 0000000..cbcd7c3 --- /dev/null +++ b/tests/api_resources/test_chat.py @@ -0,0 +1,183 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from docstrange import Docstrange, AsyncDocstrange +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestChat: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_completion_with_all_params(self, client: Docstrange) -> None: + chat = client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "https://example.com/doc.pdf"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "file_url", + }, + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "Extract the text from the document.", + "type": "text", + }, + ], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + stream=False, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_completion(self, client: Docstrange) -> None: + response = client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_completion(self, client: Docstrange) -> None: + with client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncChat: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_completion_with_all_params(self, async_client: AsyncDocstrange) -> None: + chat = await async_client.chat.create_completion( + messages=[ + { + "content": [ + { + "file_url": {"url": "https://example.com/doc.pdf"}, + "image_url": {"url": "url"}, + "text": "text", + "type": "file_url", + }, + { + "file_url": {"url": "url"}, + "image_url": {"url": "url"}, + "text": "Extract the text from the document.", + "type": "text", + }, + ], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + stream=False, + ) + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_completion(self, async_client: AsyncDocstrange) -> None: + response = await async_client.chat.with_raw_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_completion(self, async_client: AsyncDocstrange) -> None: + async with async_client.chat.with_streaming_response.create_completion( + messages=[ + { + "content": [{}, {}], + "role": "user", + } + ], + model="nanonets/Nanonets-OCR-s", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chat = await response.parse() + assert_matches_type(object, chat, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_classify.py b/tests/api_resources/test_classify.py index 63539df..b4377f6 100644 --- a/tests/api_resources/test_classify.py +++ b/tests/api_resources/test_classify.py @@ -9,7 +9,7 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ClassifyResponse +from docstrange.types import ClassifyResponse, BatchClassifyResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,11 +17,48 @@ class TestClassify: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + classify = client.classify.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.classify.with_raw_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.classify.with_streaming_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: classify = client.classify.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -30,7 +67,7 @@ def test_method_sync(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.classify.with_raw_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -43,7 +80,7 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.classify.with_streaming_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: assert not response.is_closed @@ -60,11 +97,48 @@ class TestAsyncClassify: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + classify = await async_client.classify.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.classify.with_raw_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.classify.with_streaming_response.batch( + categories='[{"name": "Invoice"}, {"name": "Contract"}, {"name": "Receipt"}]', + files=[b"raw file contents"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + classify = await response.parse() + assert_matches_type(BatchClassifyResponse, classify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: classify = await async_client.classify.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) assert_matches_type(ClassifyResponse, classify, path=["response"]) @@ -73,7 +147,7 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.classify.with_raw_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) @@ -86,7 +160,7 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.classify.with_streaming_response.sync( - categories="categories", + categories='[{"name": "Invoice", "description": "Bills and invoices"}, {"name": "Contract", "description": "Legal agreements"}]', file=b"raw file contents", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index d911970..ffdac33 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,7 +9,10 @@ from docstrange import Docstrange, AsyncDocstrange from tests.utils import assert_matches_type -from docstrange.types import ExtractResponse +from docstrange.types import ( + ExtractResponse, + BatchExtractResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,11 +20,161 @@ class TestExtract: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async(self, client: Docstrange) -> None: + extract = client.extract.async_( + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_async_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.async_( + output_format="markdown", + csv_options="", + custom_instructions="", + file=b"raw file contents", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_async(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.async_( + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_async(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.async_( + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_batch_with_all_params(self, client: Docstrange) -> None: + extract = client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_batch(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_batch(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + output_format="markdown", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_stream_with_all_params(self, client: Docstrange) -> None: + extract_stream = client.extract.stream( + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file=b"raw file contents", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + extract_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_raw_response_stream(self, client: Docstrange) -> None: + response = client.extract.with_raw_response.stream( + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_streaming_response_stream(self, client: Docstrange) -> None: + with client.extract.with_streaming_response.stream( + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sync(self, client: Docstrange) -> None: extract = client.extract.sync( - file=b"raw file contents", output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -30,10 +183,10 @@ def test_method_sync(self, client: Docstrange) -> None: @parametrize def test_method_sync_with_all_params(self, client: Docstrange) -> None: extract = client.extract.sync( - file=b"raw file contents", output_format="markdown", csv_options="", custom_instructions="", + file=b"raw file contents", file_base64="", file_url="", include_metadata="", @@ -46,7 +199,6 @@ def test_method_sync_with_all_params(self, client: Docstrange) -> None: @parametrize def test_raw_response_sync(self, client: Docstrange) -> None: response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", ) @@ -59,7 +211,6 @@ def test_raw_response_sync(self, client: Docstrange) -> None: @parametrize def test_streaming_response_sync(self, client: Docstrange) -> None: with client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown", ) as response: assert not response.is_closed @@ -76,11 +227,161 @@ class TestAsyncExtract: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + output_format="markdown", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_async_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.async_( + output_format="markdown", + csv_options="", + custom_instructions="", + file=b"raw file contents", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_async(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.async_( + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_async(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.async_( + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(ExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_batch_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract = await async_client.extract.batch( + files=[b"raw file contents"], + output_format="markdown", + csv_options="csv_options", + custom_instructions="custom_instructions", + include_metadata="include_metadata", + json_options="json_options", + prompt_mode="append", + ) + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_batch(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_batch(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.batch( + files=[b"raw file contents"], + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extract = await response.parse() + assert_matches_type(BatchExtractResponse, extract, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + output_format="markdown", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncDocstrange) -> None: + extract_stream = await async_client.extract.stream( + output_format="markdown", + csv_options="", + custom_instructions="", + enable_streaming=True, + file=b"raw file contents", + file_base64="", + file_url="", + include_metadata="", + json_options="", + prompt_mode="append", + ) + await extract_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncDocstrange) -> None: + response = await async_client.extract.with_raw_response.stream( + output_format="markdown", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncDocstrange) -> None: + async with async_client.extract.with_streaming_response.stream( + output_format="markdown", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sync(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( - file=b"raw file contents", output_format="markdown", ) assert_matches_type(ExtractResponse, extract, path=["response"]) @@ -89,10 +390,10 @@ async def test_method_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) -> None: extract = await async_client.extract.sync( - file=b"raw file contents", output_format="markdown", csv_options="", custom_instructions="", + file=b"raw file contents", file_base64="", file_url="", include_metadata="", @@ -105,7 +406,6 @@ async def test_method_sync_with_all_params(self, async_client: AsyncDocstrange) @parametrize async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: response = await async_client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", ) @@ -118,7 +418,6 @@ async def test_raw_response_sync(self, async_client: AsyncDocstrange) -> None: @parametrize async def test_streaming_response_sync(self, async_client: AsyncDocstrange) -> None: async with async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown", ) as response: assert not response.is_closed diff --git a/tests/test_client.py b/tests/test_client.py index 719c826..3d576f6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,7 +862,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() + client.extract.with_streaming_response.sync(output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @@ -872,7 +872,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.extract.with_streaming_response.sync(file=b"raw file contents", output_format="markdown").__enter__() + client.extract.with_streaming_response.sync(output_format="markdown").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -901,7 +901,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") + response = client.extract.with_raw_response.sync(output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -926,7 +926,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -951,7 +951,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1768,9 +1768,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown" - ).__aenter__() + await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() assert _get_open_connections(async_client) == 0 @@ -1782,9 +1780,7 @@ async def test_retrying_status_errors_doesnt_leak( respx_mock.post("/api/v1/extract/sync").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.extract.with_streaming_response.sync( - file=b"raw file contents", output_format="markdown" - ).__aenter__() + await async_client.extract.with_streaming_response.sync(output_format="markdown").__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1813,7 +1809,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) - response = await client.extract.with_raw_response.sync(file=b"raw file contents", output_format="markdown") + response = await client.extract.with_raw_response.sync(output_format="markdown") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1838,7 +1834,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} + output_format="markdown", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1863,7 +1859,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/api/v1/extract/sync").mock(side_effect=retry_handler) response = await client.extract.with_raw_response.sync( - file=b"raw file contents", output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} + output_format="markdown", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From e672affb60919120a36dac2ddb58c52ab8c80ded Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:34:29 +0000 Subject: [PATCH 24/26] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 6864372..7d71630 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 1804754b97b5ccf95ad31183c74527bc +config_hash: 61ade82f6ddf75edd008789d19f04a3a From 8e487a02975b8b2873fdcbf51df22a2c7f04275d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 01:10:33 +0000 Subject: [PATCH 25/26] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 5 ++--- .github/workflows/release-doctor.yml | 2 ++ .stats.yml | 2 +- bin/check-release-environment | 4 ++++ bin/publish-pypi | 6 +----- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index e1fd4b7..1be9543 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -12,9 +12,6 @@ jobs: publish: name: publish runs-on: ubuntu-latest - permissions: - contents: read - id-token: write steps: - uses: actions/checkout@v6 @@ -27,3 +24,5 @@ jobs: - name: Publish to PyPI run: | bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 5be741d..7f49e8e 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -17,3 +17,5 @@ jobs: - name: Check release environment run: | bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.DOCSTRANGE_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.stats.yml b/.stats.yml index 7d71630..e4e127d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 9 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/nanonets%2Fdocstrange-a418fe45369669cc2d14b549ee010f3a7ee52f6e47fea3489e560d33064be099.yml openapi_spec_hash: 02f7f52faae1eb42188c290c32c25f10 -config_hash: 61ade82f6ddf75edd008789d19f04a3a +config_hash: b1979c16c99baf59cbbf8c7944a47297 diff --git a/bin/check-release-environment b/bin/check-release-environment index 1e951e9..b845b0f 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,6 +2,10 @@ errors=() +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + lenErrors=${#errors[@]} if [[ lenErrors -gt 0 ]]; then diff --git a/bin/publish-pypi b/bin/publish-pypi index 5895700..e72ca2f 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -4,8 +4,4 @@ set -eux rm -rf dist mkdir -p dist uv build -if [ -n "${PYPI_TOKEN:-}" ]; then - uv publish --token=$PYPI_TOKEN -else - uv publish -fi +uv publish --token=$PYPI_TOKEN From f0813d5b6bca11bc5db2f9f12b5b1418792f7392 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 01:10:50 +0000 Subject: [PATCH 26/26] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/docstrange/_version.py | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1332969..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1da4dde --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +## 0.1.0 (2026-02-13) + +Full Changelog: [v0.0.1...v0.1.0](https://github.com/NanoNets/docstrange-python/compare/v0.0.1...v0.1.0) + +### Features + +* **api:** api update ([b03ae9b](https://github.com/NanoNets/docstrange-python/commit/b03ae9b0d7d5940df2aaff8b4cfa22c163a31b6b)) +* **api:** api update ([5d5fe8c](https://github.com/NanoNets/docstrange-python/commit/5d5fe8c9980e169a262eeed5601a568b8688a666)) +* **api:** api update ([f6e304a](https://github.com/NanoNets/docstrange-python/commit/f6e304ad4aea0364c89273b1a17f53cf730c9f09)) +* **api:** api update ([55c65e1](https://github.com/NanoNets/docstrange-python/commit/55c65e11f1cbfcd60df5ec2d65c503330bc29a3c)) +* **api:** api update ([0cb4863](https://github.com/NanoNets/docstrange-python/commit/0cb48631aa0c08788f26a3abb051fef2c12aaa20)) +* **api:** api update ([cee4e51](https://github.com/NanoNets/docstrange-python/commit/cee4e51ef55edee2402626eeb04dfe74f41c4676)) +* **api:** api update ([9cfaf51](https://github.com/NanoNets/docstrange-python/commit/9cfaf51d7c188ab63fc6352d6a689e5c0a8dbe58)) +* **api:** api update ([9da26d4](https://github.com/NanoNets/docstrange-python/commit/9da26d4a173539f2a94608833b3002c5bf4d7f37)) +* **api:** api update ([a3d7c23](https://github.com/NanoNets/docstrange-python/commit/a3d7c23e4f6027cb4eabb422a08aae1f1f4db511)) +* **api:** api update ([a1ada95](https://github.com/NanoNets/docstrange-python/commit/a1ada95fffb116ab7c5a59f5d69eb24fa69f5827)) +* **api:** api update ([3d12679](https://github.com/NanoNets/docstrange-python/commit/3d12679a9c3ff3c58563bc17c15fab514dd3aa36)) + + +### Chores + +* **internal:** fix lint error on Python 3.14 ([e1b836c](https://github.com/NanoNets/docstrange-python/commit/e1b836c1338d0c34eeae2156a19ae1a9a68b9269)) +* update SDK settings ([8e487a0](https://github.com/NanoNets/docstrange-python/commit/8e487a02975b8b2873fdcbf51df22a2c7f04275d)) +* update SDK settings ([e231259](https://github.com/NanoNets/docstrange-python/commit/e231259dabdfcb1ee3d818f5720d484dbc48d7fd)) +* update SDK settings ([ec90172](https://github.com/NanoNets/docstrange-python/commit/ec90172800803c48e72e6105e265ccd757f80314)) +* update SDK settings ([c2d9004](https://github.com/NanoNets/docstrange-python/commit/c2d9004668a9eb79f6cc5a19879ed8d9928f316f)) +* update SDK settings ([2c177c7](https://github.com/NanoNets/docstrange-python/commit/2c177c784f8492fd77ae6371e2cf229f5fd6d151)) +* update SDK settings ([19fe0dd](https://github.com/NanoNets/docstrange-python/commit/19fe0ddf024bf761e50dc300b02903068ea3a2cf)) diff --git a/pyproject.toml b/pyproject.toml index 0d11a7e..de2c1db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "docstrange-api" -version = "0.0.1" +version = "0.1.0" description = "The official Python library for the docstrange API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/docstrange/_version.py b/src/docstrange/_version.py index fc8a3e5..3393853 100644 --- a/src/docstrange/_version.py +++ b/src/docstrange/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "docstrange" -__version__ = "0.0.1" # x-release-please-version +__version__ = "0.1.0" # x-release-please-version