diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 445313c5..48105179 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/raindrop-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -44,7 +44,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/raindrop-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -63,7 +63,7 @@ jobs: - name: Get GitHub OIDC Token if: github.repository == 'stainless-sdks/raindrop-python' id: github-oidc - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -81,7 +81,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/raindrop-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 7750919d..bc301ee5 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index f8b44135..65dc4c23 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'LiquidMetal-AI/lm-raindrop-python-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Check release environment run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 874a4c0f..6db19b95 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.6.43" + ".": "0.17.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2de46eee..b144fe2b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 27 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/liquidmetal-ai%2Fraindrop-ed25b942116773db529b434ba4c6abb795db546c2d2a8ee6b6bee0e6365bbcd1.yml -openapi_spec_hash: 62b640c720a402ad73f44dd28e72440c -config_hash: 5f331f84beb1a8b8e9db4088909d4533 +configured_endpoints: 36 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/liquidmetal-ai%2Fraindrop-5cf76019d5bb0b47bae031ba30cd289190a9e6a9e0592d0a82b404b5ef7c1d12.yml +openapi_spec_hash: 7f83735738a64d49551a9a4a0b3be1b0 +config_hash: 2b17c03801f5ecdac15d3c08fe63b0af diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3ec2bc..9b4d84a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,64 @@ # Changelog +## 0.17.0 (2026-02-19) + +Full Changelog: [v0.6.43...v0.17.0](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/compare/v0.6.43...v0.17.0) + +### Features + +* **api:** updating API spec with latest changes ([33d7b9f](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/33d7b9f2343d1c6ff1c659ede15cd1264a4802cb)) +* **api:** updating API spec with latest changes ([66c035d](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/66c035d4bc3b03567135556f3d820177a78dffa7)) +* **api:** updating API spec with latest changes ([8eccdf9](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/8eccdf98d50a0dcd0b927bf3512c1945b5483355)) +* **api:** updating API spec with latest changes ([6ca79a6](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/6ca79a6059d9b2b04c2b4faac11dac498b81e16d)) +* **api:** updating API spec with latest changes ([8d39fd0](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/8d39fd09f5a7d630f3d1a89cf38bbef141fb3698)) +* **api:** updating API spec with latest changes ([f9a8088](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/f9a808854115b3623c18c0535c593955caf2e219)) +* **api:** updating API spec with latest changes ([2d9d45a](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/2d9d45aa4b9c83d5b0568c97c7d20b8ea79e7ed7)) +* **api:** updating API spec with latest changes ([196989d](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/196989d6569e3ffa9ae6b50c8f74dd1bd0fe9177)) +* **api:** updating API spec with latest changes ([55ab316](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/55ab316a91254d3efd4b6cfd87aa0ec4cea0e228)) +* **api:** updating API spec with latest changes ([4f2685e](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/4f2685ee86f7197e7a1e03579a05835089a75d4a)) +* **api:** updating API spec with latest changes ([a695b56](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/a695b564fef2ecafba1e8e89445faeb9518fa3fd)) +* **api:** updating API spec with latest changes ([70c5279](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/70c52795c14e0a0eb2a141d12728e3385c4c0fde)) +* **api:** updating API spec with latest changes ([3d5cb8a](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/3d5cb8a18075dca829db4c1171929b165e1bb60f)) +* **api:** updating API spec with latest changes ([d692016](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/d692016bda5d3877fe71d08e541b9d80b70a03f6)) +* **api:** updating API spec with latest changes ([18f97b1](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/18f97b169075c2bbb919a7706acd8b1886885380)) +* **api:** updating API spec with latest changes ([c10159f](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/c10159fc93a914c611c623fcfbd7c90f2024edeb)) +* **api:** updating API spec with latest changes ([c0b00b1](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/c0b00b1da17be5945e01a74ab86e89efcae37b85)) +* **api:** updating API spec with latest changes ([3e32b23](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/3e32b23ed54e80a40059375fd81dddb861399137)) +* **api:** updating API spec with latest changes ([a43ddc1](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/a43ddc1b1306fc987e00347ac75a25e518fd8b70)) +* **api:** updating API spec with latest changes ([d73de06](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/d73de063b634d5b009a12deddee012ca5eef967b)) +* **client:** add custom JSON encoder for extended type support ([025c238](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/025c2380fff511046fb4b94ac4d2611dfb4fffdf)) +* **client:** add support for binary request streaming ([4604c3c](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/4604c3cef2ee376ec2907f5e6225f1b5c18958cf)) + + +### Bug Fixes + +* **client:** close streams without requiring full consumption ([9f6b474](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/9f6b47440ca3512d710b3eef3c50eda3f4e4aec5)) +* compat with Python 3.14 ([2f9d169](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/2f9d16986693ecf91adba5a2f1a769917f29d402)) +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([d8c138e](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/d8c138e926f23939e859ec758f502e5641c70f15)) +* ensure streams are always closed ([6666820](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/666682014e7138336179c607980a337fce213d1a)) +* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([212ff07](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/212ff07721b9d4018400a82061976f44343cd0d8)) +* use async_to_httpx_files in patch method ([eb4fa2b](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/eb4fa2b0780b0c373c4b43a5c3fbb4d2d1e6de48)) + + +### Chores + +* add missing docstrings ([86a8870](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/86a8870d2907c05f0f351b27e4431af04d07e8f7)) +* add Python 3.14 classifier and testing ([e76a040](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/e76a0402b9420192c936d95cc1ca3cf02226a638)) +* bump `httpx-aiohttp` version to 0.1.9 ([087c121](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/087c121009a3c7bcfe0f51d5e8bb61adaa788ea1)) +* **ci:** upgrade `actions/github-script` ([4b38394](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/4b383944561f6c74e6b01d14072fdf06fc5b5fca)) +* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([8be1a4c](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/8be1a4cc833bd4e53051fa122a930f5a79ec5699)) +* format all `api.md` files ([30618d0](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/30618d0e67579f30e3458bcc44925f1a2e2d37ac)) +* **internal/tests:** avoid race condition with implicit client cleanup ([ecbcd62](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/ecbcd62c4772af8187731015598a016229ac07eb)) +* **internal:** add `--fix` argument to lint script ([d094255](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/d0942552d86c89e43563a6d6c845ea53f0646c9e)) +* **internal:** add missing files argument to base client ([9a7d8bd](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/9a7d8bdcfd022647afb68313357edfea93ef9b3a)) +* **internal:** bump dependencies ([ed9db87](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/ed9db8703dc2a69826c284f4b2cd1c11ce20f036)) +* **internal:** fix lint error on Python 3.14 ([388cded](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/388cded2b38f8c115992d0befc492684606795b6)) +* **internal:** grammar fix (it's -> its) ([bc72806](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/bc72806b1527391f965f2e70714b134093f20fe4)) +* **internal:** update `actions/checkout` version ([9c6f869](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/9c6f8690b61a76323c24148a4f54b6d3744f7c8a)) +* **package:** drop Python 3.8 support ([ae0e46a](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/ae0e46a0eb610831100c8d19d289b9201572d60e)) +* speedup initial import ([75bc3bf](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/75bc3bfaadb8205b13c21838d9ddd516f8a3b90c)) +* update lockfile ([a430dff](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/commit/a430dff85a2bdfe274d583ff386ff380acfe6e0e)) + ## 0.6.43 (2025-10-16) Full Changelog: [v0.6.42...v0.6.43](https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk/compare/v0.6.42...v0.6.43) diff --git a/LICENSE b/LICENSE index c31ae858..df4b6533 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Raindrop + Copyright 2026 Raindrop Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index e20dd7d2..32af8427 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://img.shields.io/pypi/v/lm-raindrop.svg?label=pypi%20(stable))](https://pypi.org/project/lm-raindrop/) -The Raindrop Python library provides convenient access to the Raindrop REST API from any Python 3.8+ +The Raindrop Python library provides convenient access to the Raindrop REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -30,7 +30,13 @@ from raindrop import Raindrop client = Raindrop() response = client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -56,7 +62,13 @@ client = AsyncRaindrop() async def main() -> None: response = await client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -93,7 +105,13 @@ async def main() -> None: http_client=DefaultAioHttpClient(), ) as client: response = await client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -207,7 +225,13 @@ client = Raindrop() try: client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -255,7 +279,13 @@ client = Raindrop( # Or, configure per-request: client.with_options(max_retries=5).query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -283,7 +313,13 @@ client = Raindrop( # Override per-request: client.with_options(timeout=5.0).query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -331,7 +367,9 @@ client = Raindrop() response = client.query.with_raw_response.document_query( bucket_location={ "bucket": { - "name": "my-smartbucket" + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } }, input="What are the key points in this document?", @@ -356,7 +394,13 @@ To stream the response body, use `.with_streaming_response` instead, which requi ```python with client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -470,7 +514,7 @@ print(raindrop.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/api.md b/api.md index e0199195..8681c63f 100644 --- a/api.md +++ b/api.md @@ -87,22 +87,35 @@ from raindrop.types import BucketListResponse, BucketGetResponse, BucketPutRespo Methods: -- client.bucket.list(\*\*params) -> BucketListResponse -- client.bucket.delete(\*\*params) -> object -- client.bucket.get(\*\*params) -> BucketGetResponse -- client.bucket.put(\*\*params) -> BucketPutResponse +- client.bucket.list(\*\*params) -> BucketListResponse +- client.bucket.delete(\*\*params) -> object +- client.bucket.get(\*\*params) -> BucketGetResponse +- client.bucket.put(\*\*params) -> BucketPutResponse + +## ByStatus + +Types: + +```python +from raindrop.types.bucket import ByStatusListObjectsResponse +``` + +Methods: + +- client.bucket.by_status.list_objects(\*\*params) -> ByStatusListObjectsResponse # PutMemory Types: ```python -from raindrop.types import PutMemoryCreateResponse +from raindrop.types import PutMemoryCreateResponse, PutMemoryCreateBatchResponse ``` Methods: - client.put_memory.create(\*\*params) -> PutMemoryCreateResponse +- client.put_memory.create_batch(\*\*params) -> PutMemoryCreateBatchResponse # GetMemory @@ -259,3 +272,87 @@ from raindrop.types import DeleteSemanticMemoryDeleteResponse Methods: - client.delete_semantic_memory.delete(\*\*params) -> DeleteSemanticMemoryDeleteResponse + +# ExecuteQuery + +Types: + +```python +from raindrop.types import ExecuteQueryExecuteResponse +``` + +Methods: + +- client.execute_query.execute(\*\*params) -> ExecuteQueryExecuteResponse + +# GetMetadata + +Types: + +```python +from raindrop.types import GetMetadataRetrieveResponse +``` + +Methods: + +- client.get_metadata.retrieve(\*\*params) -> GetMetadataRetrieveResponse + +# UpdateMetadata + +Types: + +```python +from raindrop.types import UpdateMetadataUpdateResponse +``` + +Methods: + +- client.update_metadata.update(\*\*params) -> UpdateMetadataUpdateResponse + +# GetPiiData + +Types: + +```python +from raindrop.types import GetPiiDataRetrieveResponse +``` + +Methods: + +- client.get_pii_data.retrieve(\*\*params) -> GetPiiDataRetrieveResponse + +# DocumentStatus + +Types: + +```python +from raindrop.types import DocumentStatusGetStatusResponse +``` + +Methods: + +- client.document_status.get_status(\*\*params) -> DocumentStatusGetStatusResponse + +# DocumentStatusBulk + +Types: + +```python +from raindrop.types import DocumentStatusBulkGetStatusBulkResponse +``` + +Methods: + +- client.document_status_bulk.get_status_bulk(\*\*params) -> DocumentStatusBulkGetStatusBulkResponse + +# RehydrationStatus + +Types: + +```python +from raindrop.types import RehydrationStatusCreateResponse +``` + +Methods: + +- client.rehydration_status.create(\*\*params) -> RehydrationStatusCreateResponse diff --git a/pyproject.toml b/pyproject.toml index a0fdcc46..8cf2596b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,30 +1,32 @@ [project] name = "lm-raindrop" -version = "0.6.43" +version = "0.17.0" description = "The official Python library for the raindrop API" dynamic = ["readme"] license = "Apache-2.0" authors = [ { name = "Raindrop", email = "" }, ] + dependencies = [ - "httpx>=0.23.0, <1", - "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", - "anyio>=3.5.0, <5", - "distro>=1.7.0, <2", - "sniffio", + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", ] -requires-python = ">= 3.8" + +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -39,14 +41,14 @@ Homepage = "https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk" Repository = "https://github.com/LiquidMetal-AI/lm-raindrop-python-sdk" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ "pyright==1.1.399", - "mypy", + "mypy==1.17", "respx", "pytest", "pytest-asyncio", @@ -67,7 +69,7 @@ format = { chain = [ # run formatting again to fix any inconsistencies when imports are stripped "format:ruff", ]} -"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'" "format:ruff" = "ruff format" "lint" = { chain = [ @@ -141,7 +143,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", diff --git a/requirements-dev.lock b/requirements-dev.lock index 9e0fb4d6..e01e25cb 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,40 +12,45 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.3 # via httpx-aiohttp # via lm-raindrop -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.1 # via httpx # via lm-raindrop -argcomplete==3.1.2 +argcomplete==3.6.3 # via nox async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 + # via nox +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2026.1.4 # via httpcore # via httpx -colorlog==6.7.0 +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 # via nox -dirty-equals==0.6.0 -distlib==0.3.7 +dirty-equals==0.11 +distlib==0.4.0 # via virtualenv -distro==1.8.0 +distro==1.9.0 # via lm-raindrop -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio # via pytest -execnet==2.1.1 +execnet==2.1.2 # via pytest-xdist -filelock==3.12.4 +filelock==3.19.1 # via virtualenv -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -56,82 +61,89 @@ httpx==0.28.1 # via httpx-aiohttp # via lm-raindrop # via respx -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.12 # via lm-raindrop -idna==3.4 +humanize==4.13.0 + # via nox +idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 +importlib-metadata==8.7.1 +iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -mypy==1.14.1 -mypy-extensions==1.0.0 +mypy==1.17.0 +mypy-extensions==1.1.0 # via mypy -nodeenv==1.8.0 +nodeenv==1.10.0 # via pyright -nox==2023.4.22 -packaging==23.2 +nox==2025.11.12 +packaging==25.0 + # via dependency-groups # via nox # via pytest -platformdirs==3.11.0 +pathspec==1.0.3 + # via mypy +platformdirs==4.4.0 # via virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via pytest -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via lm-raindrop -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -pygments==2.18.0 +pygments==2.19.2 + # via pytest # via rich pyright==1.1.399 -pytest==8.3.3 +pytest==8.4.2 # via pytest-asyncio # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 # via time-machine -pytz==2023.3.post1 - # via dirty-equals respx==0.22.0 -rich==13.7.1 -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 +rich==14.2.0 +ruff==0.14.13 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via lm-raindrop -time-machine==2.9.0 -tomli==2.0.2 +time-machine==2.19.0 +tomli==2.4.0 + # via dependency-groups # via mypy + # via nox # via pytest -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via lm-raindrop # via multidict # via mypy # via pydantic # via pydantic-core # via pyright + # via pytest-asyncio # via typing-inspection -typing-inspection==0.4.1 + # via virtualenv +typing-inspection==0.4.2 # via pydantic -virtualenv==20.24.5 +virtualenv==20.36.1 # via nox -yarl==1.20.0 +yarl==1.22.0 # via aiohttp -zipp==3.17.0 +zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index a0101301..0fba1f57 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,28 +12,28 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.3 # via httpx-aiohttp # via lm-raindrop -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.1 # via httpx # via lm-raindrop async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 +certifi==2026.1.4 # via httpcore # via httpx -distro==1.8.0 +distro==1.9.0 # via lm-raindrop -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -43,33 +43,34 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via lm-raindrop -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.12 # via lm-raindrop -idna==3.4 +idna==3.11 # via anyio # via httpx # via yarl -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via lm-raindrop -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via lm-raindrop -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via lm-raindrop # via multidict # via pydantic # via pydantic-core # via typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via pydantic -yarl==1.20.0 +yarl==1.22.0 # via aiohttp diff --git a/scripts/lint b/scripts/lint index 06c31e3f..a0b11fb1 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,8 +4,13 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running lints" -rye run lint +if [ "$1" = "--fix" ]; then + echo "==> Running lints with --fix" + rye run fix:ruff +else + echo "==> Running lints" + rye run lint +fi echo "==> Making sure it imports" rye run python -c 'import raindrop' diff --git a/src/raindrop/_base_client.py b/src/raindrop/_base_client.py index 6de501c9..27affd79 100644 --- a/src/raindrop/_base_client.py +++ b/src/raindrop/_base_client.py @@ -9,6 +9,7 @@ import inspect import logging import platform +import warnings import email.utils from types import TracebackType from random import random @@ -51,9 +52,11 @@ ResponseT, AnyMapping, PostParser, + BinaryTypes, RequestFiles, HttpxSendArgs, RequestOptions, + AsyncBinaryTypes, HttpxRequestFiles, ModelBuilderProtocol, not_given, @@ -83,6 +86,7 @@ APIConnectionError, APIResponseValidationError, ) +from ._utils._json import openapi_dumps log: logging.Logger = logging.getLogger(__name__) @@ -477,8 +481,19 @@ def _build_request( retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): - log.debug("Request options: %s", model_dump(options, exclude_unset=True)) - + log.debug( + "Request options: %s", + model_dump( + options, + exclude_unset=True, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) kwargs: dict[str, Any] = {} json_data = options.json_data @@ -532,10 +547,18 @@ def _build_request( is_body_allowed = options.method.lower() != "get" if is_body_allowed: - if isinstance(json_data, bytes): + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): kwargs["content"] = json_data - else: - kwargs["json"] = json_data if is_given(json_data) else None + elif not files: + # Don't set content when JSON is sent as multipart/form-data, + # since httpx's content param overrides other body arguments + kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None kwargs["files"] = files else: headers.pop("Content-Type", None) @@ -1194,6 +1217,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, @@ -1206,6 +1230,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[True], @@ -1219,6 +1244,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool, @@ -1231,13 +1257,25 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @@ -1247,9 +1285,24 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) return self.request(cast_to, opts) def put( @@ -1258,11 +1311,23 @@ def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1272,9 +1337,19 @@ def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return self.request(cast_to, opts) def get_api_list( @@ -1714,6 +1789,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, @@ -1726,6 +1802,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[True], @@ -1739,6 +1816,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool, @@ -1751,13 +1829,25 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @@ -1767,9 +1857,29 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, + ) return await self.request(cast_to, opts) async def put( @@ -1778,11 +1888,23 @@ async def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) @@ -1792,9 +1914,19 @@ async def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return await self.request(cast_to, opts) def get_api_list( diff --git a/src/raindrop/_client.py b/src/raindrop/_client.py index 6b324668..1bc45335 100644 --- a/src/raindrop/_client.py +++ b/src/raindrop/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Mapping +from typing import TYPE_CHECKING, Any, Mapping from typing_extensions import Self, override import httpx @@ -20,24 +20,8 @@ not_given, ) from ._utils import is_given, get_async_library +from ._compat import cached_property from ._version import __version__ -from .resources import ( - bucket, - get_memory, - put_memory, - end_session, - delete_memory, - get_procedure, - put_procedure, - start_session, - list_procedures, - delete_procedure, - summarize_memory, - rehydrate_session, - get_semantic_memory, - put_semantic_memory, - delete_semantic_memory, -) from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import RaindropError, APIStatusError from ._base_client import ( @@ -45,7 +29,56 @@ SyncAPIClient, AsyncAPIClient, ) -from .resources.query import query + +if TYPE_CHECKING: + from .resources import ( + query, + bucket, + get_memory, + put_memory, + end_session, + get_metadata, + get_pii_data, + delete_memory, + execute_query, + get_procedure, + put_procedure, + start_session, + document_status, + list_procedures, + update_metadata, + delete_procedure, + summarize_memory, + rehydrate_session, + rehydration_status, + get_semantic_memory, + put_semantic_memory, + document_status_bulk, + delete_semantic_memory, + ) + from .resources.get_memory import GetMemoryResource, AsyncGetMemoryResource + from .resources.put_memory import PutMemoryResource, AsyncPutMemoryResource + from .resources.end_session import EndSessionResource, AsyncEndSessionResource + from .resources.query.query import QueryResource, AsyncQueryResource + from .resources.get_metadata import GetMetadataResource, AsyncGetMetadataResource + from .resources.get_pii_data import GetPiiDataResource, AsyncGetPiiDataResource + from .resources.bucket.bucket import BucketResource, AsyncBucketResource + from .resources.delete_memory import DeleteMemoryResource, AsyncDeleteMemoryResource + from .resources.execute_query import ExecuteQueryResource, AsyncExecuteQueryResource + from .resources.get_procedure import GetProcedureResource, AsyncGetProcedureResource + from .resources.put_procedure import PutProcedureResource, AsyncPutProcedureResource + from .resources.start_session import StartSessionResource, AsyncStartSessionResource + from .resources.document_status import DocumentStatusResource, AsyncDocumentStatusResource + from .resources.list_procedures import ListProceduresResource, AsyncListProceduresResource + from .resources.update_metadata import UpdateMetadataResource, AsyncUpdateMetadataResource + from .resources.delete_procedure import DeleteProcedureResource, AsyncDeleteProcedureResource + from .resources.summarize_memory import SummarizeMemoryResource, AsyncSummarizeMemoryResource + from .resources.rehydrate_session import RehydrateSessionResource, AsyncRehydrateSessionResource + from .resources.rehydration_status import RehydrationStatusResource, AsyncRehydrationStatusResource + from .resources.get_semantic_memory import GetSemanticMemoryResource, AsyncGetSemanticMemoryResource + from .resources.put_semantic_memory import PutSemanticMemoryResource, AsyncPutSemanticMemoryResource + from .resources.document_status_bulk import DocumentStatusBulkResource, AsyncDocumentStatusBulkResource + from .resources.delete_semantic_memory import DeleteSemanticMemoryResource, AsyncDeleteSemanticMemoryResource __all__ = [ "Timeout", @@ -60,25 +93,6 @@ class Raindrop(SyncAPIClient): - query: query.QueryResource - bucket: bucket.BucketResource - put_memory: put_memory.PutMemoryResource - get_memory: get_memory.GetMemoryResource - delete_memory: delete_memory.DeleteMemoryResource - summarize_memory: summarize_memory.SummarizeMemoryResource - start_session: start_session.StartSessionResource - end_session: end_session.EndSessionResource - rehydrate_session: rehydrate_session.RehydrateSessionResource - put_procedure: put_procedure.PutProcedureResource - get_procedure: get_procedure.GetProcedureResource - delete_procedure: delete_procedure.DeleteProcedureResource - list_procedures: list_procedures.ListProceduresResource - put_semantic_memory: put_semantic_memory.PutSemanticMemoryResource - get_semantic_memory: get_semantic_memory.GetSemanticMemoryResource - delete_semantic_memory: delete_semantic_memory.DeleteSemanticMemoryResource - with_raw_response: RaindropWithRawResponse - with_streaming_response: RaindropWithStreamedResponse - # client options api_key: str @@ -133,24 +147,151 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.query = query.QueryResource(self) - self.bucket = bucket.BucketResource(self) - self.put_memory = put_memory.PutMemoryResource(self) - self.get_memory = get_memory.GetMemoryResource(self) - self.delete_memory = delete_memory.DeleteMemoryResource(self) - self.summarize_memory = summarize_memory.SummarizeMemoryResource(self) - self.start_session = start_session.StartSessionResource(self) - self.end_session = end_session.EndSessionResource(self) - self.rehydrate_session = rehydrate_session.RehydrateSessionResource(self) - self.put_procedure = put_procedure.PutProcedureResource(self) - self.get_procedure = get_procedure.GetProcedureResource(self) - self.delete_procedure = delete_procedure.DeleteProcedureResource(self) - self.list_procedures = list_procedures.ListProceduresResource(self) - self.put_semantic_memory = put_semantic_memory.PutSemanticMemoryResource(self) - self.get_semantic_memory = get_semantic_memory.GetSemanticMemoryResource(self) - self.delete_semantic_memory = delete_semantic_memory.DeleteSemanticMemoryResource(self) - self.with_raw_response = RaindropWithRawResponse(self) - self.with_streaming_response = RaindropWithStreamedResponse(self) + @cached_property + def query(self) -> QueryResource: + from .resources.query import QueryResource + + return QueryResource(self) + + @cached_property + def bucket(self) -> BucketResource: + from .resources.bucket import BucketResource + + return BucketResource(self) + + @cached_property + def put_memory(self) -> PutMemoryResource: + from .resources.put_memory import PutMemoryResource + + return PutMemoryResource(self) + + @cached_property + def get_memory(self) -> GetMemoryResource: + from .resources.get_memory import GetMemoryResource + + return GetMemoryResource(self) + + @cached_property + def delete_memory(self) -> DeleteMemoryResource: + from .resources.delete_memory import DeleteMemoryResource + + return DeleteMemoryResource(self) + + @cached_property + def summarize_memory(self) -> SummarizeMemoryResource: + from .resources.summarize_memory import SummarizeMemoryResource + + return SummarizeMemoryResource(self) + + @cached_property + def start_session(self) -> StartSessionResource: + from .resources.start_session import StartSessionResource + + return StartSessionResource(self) + + @cached_property + def end_session(self) -> EndSessionResource: + from .resources.end_session import EndSessionResource + + return EndSessionResource(self) + + @cached_property + def rehydrate_session(self) -> RehydrateSessionResource: + from .resources.rehydrate_session import RehydrateSessionResource + + return RehydrateSessionResource(self) + + @cached_property + def put_procedure(self) -> PutProcedureResource: + from .resources.put_procedure import PutProcedureResource + + return PutProcedureResource(self) + + @cached_property + def get_procedure(self) -> GetProcedureResource: + from .resources.get_procedure import GetProcedureResource + + return GetProcedureResource(self) + + @cached_property + def delete_procedure(self) -> DeleteProcedureResource: + from .resources.delete_procedure import DeleteProcedureResource + + return DeleteProcedureResource(self) + + @cached_property + def list_procedures(self) -> ListProceduresResource: + from .resources.list_procedures import ListProceduresResource + + return ListProceduresResource(self) + + @cached_property + def put_semantic_memory(self) -> PutSemanticMemoryResource: + from .resources.put_semantic_memory import PutSemanticMemoryResource + + return PutSemanticMemoryResource(self) + + @cached_property + def get_semantic_memory(self) -> GetSemanticMemoryResource: + from .resources.get_semantic_memory import GetSemanticMemoryResource + + return GetSemanticMemoryResource(self) + + @cached_property + def delete_semantic_memory(self) -> DeleteSemanticMemoryResource: + from .resources.delete_semantic_memory import DeleteSemanticMemoryResource + + return DeleteSemanticMemoryResource(self) + + @cached_property + def execute_query(self) -> ExecuteQueryResource: + from .resources.execute_query import ExecuteQueryResource + + return ExecuteQueryResource(self) + + @cached_property + def get_metadata(self) -> GetMetadataResource: + from .resources.get_metadata import GetMetadataResource + + return GetMetadataResource(self) + + @cached_property + def update_metadata(self) -> UpdateMetadataResource: + from .resources.update_metadata import UpdateMetadataResource + + return UpdateMetadataResource(self) + + @cached_property + def get_pii_data(self) -> GetPiiDataResource: + from .resources.get_pii_data import GetPiiDataResource + + return GetPiiDataResource(self) + + @cached_property + def document_status(self) -> DocumentStatusResource: + from .resources.document_status import DocumentStatusResource + + return DocumentStatusResource(self) + + @cached_property + def document_status_bulk(self) -> DocumentStatusBulkResource: + from .resources.document_status_bulk import DocumentStatusBulkResource + + return DocumentStatusBulkResource(self) + + @cached_property + def rehydration_status(self) -> RehydrationStatusResource: + from .resources.rehydration_status import RehydrationStatusResource + + return RehydrationStatusResource(self) + + @cached_property + def with_raw_response(self) -> RaindropWithRawResponse: + return RaindropWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RaindropWithStreamedResponse: + return RaindropWithStreamedResponse(self) @property @override @@ -258,25 +399,6 @@ def _make_status_error( class AsyncRaindrop(AsyncAPIClient): - query: query.AsyncQueryResource - bucket: bucket.AsyncBucketResource - put_memory: put_memory.AsyncPutMemoryResource - get_memory: get_memory.AsyncGetMemoryResource - delete_memory: delete_memory.AsyncDeleteMemoryResource - summarize_memory: summarize_memory.AsyncSummarizeMemoryResource - start_session: start_session.AsyncStartSessionResource - end_session: end_session.AsyncEndSessionResource - rehydrate_session: rehydrate_session.AsyncRehydrateSessionResource - put_procedure: put_procedure.AsyncPutProcedureResource - get_procedure: get_procedure.AsyncGetProcedureResource - delete_procedure: delete_procedure.AsyncDeleteProcedureResource - list_procedures: list_procedures.AsyncListProceduresResource - put_semantic_memory: put_semantic_memory.AsyncPutSemanticMemoryResource - get_semantic_memory: get_semantic_memory.AsyncGetSemanticMemoryResource - delete_semantic_memory: delete_semantic_memory.AsyncDeleteSemanticMemoryResource - with_raw_response: AsyncRaindropWithRawResponse - with_streaming_response: AsyncRaindropWithStreamedResponse - # client options api_key: str @@ -331,24 +453,151 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.query = query.AsyncQueryResource(self) - self.bucket = bucket.AsyncBucketResource(self) - self.put_memory = put_memory.AsyncPutMemoryResource(self) - self.get_memory = get_memory.AsyncGetMemoryResource(self) - self.delete_memory = delete_memory.AsyncDeleteMemoryResource(self) - self.summarize_memory = summarize_memory.AsyncSummarizeMemoryResource(self) - self.start_session = start_session.AsyncStartSessionResource(self) - self.end_session = end_session.AsyncEndSessionResource(self) - self.rehydrate_session = rehydrate_session.AsyncRehydrateSessionResource(self) - self.put_procedure = put_procedure.AsyncPutProcedureResource(self) - self.get_procedure = get_procedure.AsyncGetProcedureResource(self) - self.delete_procedure = delete_procedure.AsyncDeleteProcedureResource(self) - self.list_procedures = list_procedures.AsyncListProceduresResource(self) - self.put_semantic_memory = put_semantic_memory.AsyncPutSemanticMemoryResource(self) - self.get_semantic_memory = get_semantic_memory.AsyncGetSemanticMemoryResource(self) - self.delete_semantic_memory = delete_semantic_memory.AsyncDeleteSemanticMemoryResource(self) - self.with_raw_response = AsyncRaindropWithRawResponse(self) - self.with_streaming_response = AsyncRaindropWithStreamedResponse(self) + @cached_property + def query(self) -> AsyncQueryResource: + from .resources.query import AsyncQueryResource + + return AsyncQueryResource(self) + + @cached_property + def bucket(self) -> AsyncBucketResource: + from .resources.bucket import AsyncBucketResource + + return AsyncBucketResource(self) + + @cached_property + def put_memory(self) -> AsyncPutMemoryResource: + from .resources.put_memory import AsyncPutMemoryResource + + return AsyncPutMemoryResource(self) + + @cached_property + def get_memory(self) -> AsyncGetMemoryResource: + from .resources.get_memory import AsyncGetMemoryResource + + return AsyncGetMemoryResource(self) + + @cached_property + def delete_memory(self) -> AsyncDeleteMemoryResource: + from .resources.delete_memory import AsyncDeleteMemoryResource + + return AsyncDeleteMemoryResource(self) + + @cached_property + def summarize_memory(self) -> AsyncSummarizeMemoryResource: + from .resources.summarize_memory import AsyncSummarizeMemoryResource + + return AsyncSummarizeMemoryResource(self) + + @cached_property + def start_session(self) -> AsyncStartSessionResource: + from .resources.start_session import AsyncStartSessionResource + + return AsyncStartSessionResource(self) + + @cached_property + def end_session(self) -> AsyncEndSessionResource: + from .resources.end_session import AsyncEndSessionResource + + return AsyncEndSessionResource(self) + + @cached_property + def rehydrate_session(self) -> AsyncRehydrateSessionResource: + from .resources.rehydrate_session import AsyncRehydrateSessionResource + + return AsyncRehydrateSessionResource(self) + + @cached_property + def put_procedure(self) -> AsyncPutProcedureResource: + from .resources.put_procedure import AsyncPutProcedureResource + + return AsyncPutProcedureResource(self) + + @cached_property + def get_procedure(self) -> AsyncGetProcedureResource: + from .resources.get_procedure import AsyncGetProcedureResource + + return AsyncGetProcedureResource(self) + + @cached_property + def delete_procedure(self) -> AsyncDeleteProcedureResource: + from .resources.delete_procedure import AsyncDeleteProcedureResource + + return AsyncDeleteProcedureResource(self) + + @cached_property + def list_procedures(self) -> AsyncListProceduresResource: + from .resources.list_procedures import AsyncListProceduresResource + + return AsyncListProceduresResource(self) + + @cached_property + def put_semantic_memory(self) -> AsyncPutSemanticMemoryResource: + from .resources.put_semantic_memory import AsyncPutSemanticMemoryResource + + return AsyncPutSemanticMemoryResource(self) + + @cached_property + def get_semantic_memory(self) -> AsyncGetSemanticMemoryResource: + from .resources.get_semantic_memory import AsyncGetSemanticMemoryResource + + return AsyncGetSemanticMemoryResource(self) + + @cached_property + def delete_semantic_memory(self) -> AsyncDeleteSemanticMemoryResource: + from .resources.delete_semantic_memory import AsyncDeleteSemanticMemoryResource + + return AsyncDeleteSemanticMemoryResource(self) + + @cached_property + def execute_query(self) -> AsyncExecuteQueryResource: + from .resources.execute_query import AsyncExecuteQueryResource + + return AsyncExecuteQueryResource(self) + + @cached_property + def get_metadata(self) -> AsyncGetMetadataResource: + from .resources.get_metadata import AsyncGetMetadataResource + + return AsyncGetMetadataResource(self) + + @cached_property + def update_metadata(self) -> AsyncUpdateMetadataResource: + from .resources.update_metadata import AsyncUpdateMetadataResource + + return AsyncUpdateMetadataResource(self) + + @cached_property + def get_pii_data(self) -> AsyncGetPiiDataResource: + from .resources.get_pii_data import AsyncGetPiiDataResource + + return AsyncGetPiiDataResource(self) + + @cached_property + def document_status(self) -> AsyncDocumentStatusResource: + from .resources.document_status import AsyncDocumentStatusResource + + return AsyncDocumentStatusResource(self) + + @cached_property + def document_status_bulk(self) -> AsyncDocumentStatusBulkResource: + from .resources.document_status_bulk import AsyncDocumentStatusBulkResource + + return AsyncDocumentStatusBulkResource(self) + + @cached_property + def rehydration_status(self) -> AsyncRehydrationStatusResource: + from .resources.rehydration_status import AsyncRehydrationStatusResource + + return AsyncRehydrationStatusResource(self) + + @cached_property + def with_raw_response(self) -> AsyncRaindropWithRawResponse: + return AsyncRaindropWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRaindropWithStreamedResponse: + return AsyncRaindropWithStreamedResponse(self) @property @override @@ -456,117 +705,583 @@ def _make_status_error( class RaindropWithRawResponse: + _client: Raindrop + def __init__(self, client: Raindrop) -> None: - self.query = query.QueryResourceWithRawResponse(client.query) - self.bucket = bucket.BucketResourceWithRawResponse(client.bucket) - self.put_memory = put_memory.PutMemoryResourceWithRawResponse(client.put_memory) - self.get_memory = get_memory.GetMemoryResourceWithRawResponse(client.get_memory) - self.delete_memory = delete_memory.DeleteMemoryResourceWithRawResponse(client.delete_memory) - self.summarize_memory = summarize_memory.SummarizeMemoryResourceWithRawResponse(client.summarize_memory) - self.start_session = start_session.StartSessionResourceWithRawResponse(client.start_session) - self.end_session = end_session.EndSessionResourceWithRawResponse(client.end_session) - self.rehydrate_session = rehydrate_session.RehydrateSessionResourceWithRawResponse(client.rehydrate_session) - self.put_procedure = put_procedure.PutProcedureResourceWithRawResponse(client.put_procedure) - self.get_procedure = get_procedure.GetProcedureResourceWithRawResponse(client.get_procedure) - self.delete_procedure = delete_procedure.DeleteProcedureResourceWithRawResponse(client.delete_procedure) - self.list_procedures = list_procedures.ListProceduresResourceWithRawResponse(client.list_procedures) - self.put_semantic_memory = put_semantic_memory.PutSemanticMemoryResourceWithRawResponse( - client.put_semantic_memory - ) - self.get_semantic_memory = get_semantic_memory.GetSemanticMemoryResourceWithRawResponse( - client.get_semantic_memory - ) - self.delete_semantic_memory = delete_semantic_memory.DeleteSemanticMemoryResourceWithRawResponse( - client.delete_semantic_memory - ) + self._client = client + + @cached_property + def query(self) -> query.QueryResourceWithRawResponse: + from .resources.query import QueryResourceWithRawResponse + + return QueryResourceWithRawResponse(self._client.query) + + @cached_property + def bucket(self) -> bucket.BucketResourceWithRawResponse: + from .resources.bucket import BucketResourceWithRawResponse + + return BucketResourceWithRawResponse(self._client.bucket) + + @cached_property + def put_memory(self) -> put_memory.PutMemoryResourceWithRawResponse: + from .resources.put_memory import PutMemoryResourceWithRawResponse + + return PutMemoryResourceWithRawResponse(self._client.put_memory) + + @cached_property + def get_memory(self) -> get_memory.GetMemoryResourceWithRawResponse: + from .resources.get_memory import GetMemoryResourceWithRawResponse + + return GetMemoryResourceWithRawResponse(self._client.get_memory) + + @cached_property + def delete_memory(self) -> delete_memory.DeleteMemoryResourceWithRawResponse: + from .resources.delete_memory import DeleteMemoryResourceWithRawResponse + + return DeleteMemoryResourceWithRawResponse(self._client.delete_memory) + + @cached_property + def summarize_memory(self) -> summarize_memory.SummarizeMemoryResourceWithRawResponse: + from .resources.summarize_memory import SummarizeMemoryResourceWithRawResponse + + return SummarizeMemoryResourceWithRawResponse(self._client.summarize_memory) + + @cached_property + def start_session(self) -> start_session.StartSessionResourceWithRawResponse: + from .resources.start_session import StartSessionResourceWithRawResponse + + return StartSessionResourceWithRawResponse(self._client.start_session) + + @cached_property + def end_session(self) -> end_session.EndSessionResourceWithRawResponse: + from .resources.end_session import EndSessionResourceWithRawResponse + + return EndSessionResourceWithRawResponse(self._client.end_session) + + @cached_property + def rehydrate_session(self) -> rehydrate_session.RehydrateSessionResourceWithRawResponse: + from .resources.rehydrate_session import RehydrateSessionResourceWithRawResponse + + return RehydrateSessionResourceWithRawResponse(self._client.rehydrate_session) + + @cached_property + def put_procedure(self) -> put_procedure.PutProcedureResourceWithRawResponse: + from .resources.put_procedure import PutProcedureResourceWithRawResponse + + return PutProcedureResourceWithRawResponse(self._client.put_procedure) + + @cached_property + def get_procedure(self) -> get_procedure.GetProcedureResourceWithRawResponse: + from .resources.get_procedure import GetProcedureResourceWithRawResponse + + return GetProcedureResourceWithRawResponse(self._client.get_procedure) + + @cached_property + def delete_procedure(self) -> delete_procedure.DeleteProcedureResourceWithRawResponse: + from .resources.delete_procedure import DeleteProcedureResourceWithRawResponse + + return DeleteProcedureResourceWithRawResponse(self._client.delete_procedure) + + @cached_property + def list_procedures(self) -> list_procedures.ListProceduresResourceWithRawResponse: + from .resources.list_procedures import ListProceduresResourceWithRawResponse + + return ListProceduresResourceWithRawResponse(self._client.list_procedures) + + @cached_property + def put_semantic_memory(self) -> put_semantic_memory.PutSemanticMemoryResourceWithRawResponse: + from .resources.put_semantic_memory import PutSemanticMemoryResourceWithRawResponse + + return PutSemanticMemoryResourceWithRawResponse(self._client.put_semantic_memory) + + @cached_property + def get_semantic_memory(self) -> get_semantic_memory.GetSemanticMemoryResourceWithRawResponse: + from .resources.get_semantic_memory import GetSemanticMemoryResourceWithRawResponse + + return GetSemanticMemoryResourceWithRawResponse(self._client.get_semantic_memory) + + @cached_property + def delete_semantic_memory(self) -> delete_semantic_memory.DeleteSemanticMemoryResourceWithRawResponse: + from .resources.delete_semantic_memory import DeleteSemanticMemoryResourceWithRawResponse + + return DeleteSemanticMemoryResourceWithRawResponse(self._client.delete_semantic_memory) + + @cached_property + def execute_query(self) -> execute_query.ExecuteQueryResourceWithRawResponse: + from .resources.execute_query import ExecuteQueryResourceWithRawResponse + + return ExecuteQueryResourceWithRawResponse(self._client.execute_query) + + @cached_property + def get_metadata(self) -> get_metadata.GetMetadataResourceWithRawResponse: + from .resources.get_metadata import GetMetadataResourceWithRawResponse + + return GetMetadataResourceWithRawResponse(self._client.get_metadata) + + @cached_property + def update_metadata(self) -> update_metadata.UpdateMetadataResourceWithRawResponse: + from .resources.update_metadata import UpdateMetadataResourceWithRawResponse + + return UpdateMetadataResourceWithRawResponse(self._client.update_metadata) + + @cached_property + def get_pii_data(self) -> get_pii_data.GetPiiDataResourceWithRawResponse: + from .resources.get_pii_data import GetPiiDataResourceWithRawResponse + + return GetPiiDataResourceWithRawResponse(self._client.get_pii_data) + + @cached_property + def document_status(self) -> document_status.DocumentStatusResourceWithRawResponse: + from .resources.document_status import DocumentStatusResourceWithRawResponse + + return DocumentStatusResourceWithRawResponse(self._client.document_status) + + @cached_property + def document_status_bulk(self) -> document_status_bulk.DocumentStatusBulkResourceWithRawResponse: + from .resources.document_status_bulk import DocumentStatusBulkResourceWithRawResponse + + return DocumentStatusBulkResourceWithRawResponse(self._client.document_status_bulk) + + @cached_property + def rehydration_status(self) -> rehydration_status.RehydrationStatusResourceWithRawResponse: + from .resources.rehydration_status import RehydrationStatusResourceWithRawResponse + + return RehydrationStatusResourceWithRawResponse(self._client.rehydration_status) class AsyncRaindropWithRawResponse: + _client: AsyncRaindrop + def __init__(self, client: AsyncRaindrop) -> None: - self.query = query.AsyncQueryResourceWithRawResponse(client.query) - self.bucket = bucket.AsyncBucketResourceWithRawResponse(client.bucket) - self.put_memory = put_memory.AsyncPutMemoryResourceWithRawResponse(client.put_memory) - self.get_memory = get_memory.AsyncGetMemoryResourceWithRawResponse(client.get_memory) - self.delete_memory = delete_memory.AsyncDeleteMemoryResourceWithRawResponse(client.delete_memory) - self.summarize_memory = summarize_memory.AsyncSummarizeMemoryResourceWithRawResponse(client.summarize_memory) - self.start_session = start_session.AsyncStartSessionResourceWithRawResponse(client.start_session) - self.end_session = end_session.AsyncEndSessionResourceWithRawResponse(client.end_session) - self.rehydrate_session = rehydrate_session.AsyncRehydrateSessionResourceWithRawResponse( - client.rehydrate_session - ) - self.put_procedure = put_procedure.AsyncPutProcedureResourceWithRawResponse(client.put_procedure) - self.get_procedure = get_procedure.AsyncGetProcedureResourceWithRawResponse(client.get_procedure) - self.delete_procedure = delete_procedure.AsyncDeleteProcedureResourceWithRawResponse(client.delete_procedure) - self.list_procedures = list_procedures.AsyncListProceduresResourceWithRawResponse(client.list_procedures) - self.put_semantic_memory = put_semantic_memory.AsyncPutSemanticMemoryResourceWithRawResponse( - client.put_semantic_memory - ) - self.get_semantic_memory = get_semantic_memory.AsyncGetSemanticMemoryResourceWithRawResponse( - client.get_semantic_memory - ) - self.delete_semantic_memory = delete_semantic_memory.AsyncDeleteSemanticMemoryResourceWithRawResponse( - client.delete_semantic_memory - ) + self._client = client + + @cached_property + def query(self) -> query.AsyncQueryResourceWithRawResponse: + from .resources.query import AsyncQueryResourceWithRawResponse + + return AsyncQueryResourceWithRawResponse(self._client.query) + + @cached_property + def bucket(self) -> bucket.AsyncBucketResourceWithRawResponse: + from .resources.bucket import AsyncBucketResourceWithRawResponse + + return AsyncBucketResourceWithRawResponse(self._client.bucket) + + @cached_property + def put_memory(self) -> put_memory.AsyncPutMemoryResourceWithRawResponse: + from .resources.put_memory import AsyncPutMemoryResourceWithRawResponse + + return AsyncPutMemoryResourceWithRawResponse(self._client.put_memory) + + @cached_property + def get_memory(self) -> get_memory.AsyncGetMemoryResourceWithRawResponse: + from .resources.get_memory import AsyncGetMemoryResourceWithRawResponse + + return AsyncGetMemoryResourceWithRawResponse(self._client.get_memory) + + @cached_property + def delete_memory(self) -> delete_memory.AsyncDeleteMemoryResourceWithRawResponse: + from .resources.delete_memory import AsyncDeleteMemoryResourceWithRawResponse + + return AsyncDeleteMemoryResourceWithRawResponse(self._client.delete_memory) + + @cached_property + def summarize_memory(self) -> summarize_memory.AsyncSummarizeMemoryResourceWithRawResponse: + from .resources.summarize_memory import AsyncSummarizeMemoryResourceWithRawResponse + + return AsyncSummarizeMemoryResourceWithRawResponse(self._client.summarize_memory) + + @cached_property + def start_session(self) -> start_session.AsyncStartSessionResourceWithRawResponse: + from .resources.start_session import AsyncStartSessionResourceWithRawResponse + + return AsyncStartSessionResourceWithRawResponse(self._client.start_session) + + @cached_property + def end_session(self) -> end_session.AsyncEndSessionResourceWithRawResponse: + from .resources.end_session import AsyncEndSessionResourceWithRawResponse + + return AsyncEndSessionResourceWithRawResponse(self._client.end_session) + + @cached_property + def rehydrate_session(self) -> rehydrate_session.AsyncRehydrateSessionResourceWithRawResponse: + from .resources.rehydrate_session import AsyncRehydrateSessionResourceWithRawResponse + + return AsyncRehydrateSessionResourceWithRawResponse(self._client.rehydrate_session) + + @cached_property + def put_procedure(self) -> put_procedure.AsyncPutProcedureResourceWithRawResponse: + from .resources.put_procedure import AsyncPutProcedureResourceWithRawResponse + + return AsyncPutProcedureResourceWithRawResponse(self._client.put_procedure) + + @cached_property + def get_procedure(self) -> get_procedure.AsyncGetProcedureResourceWithRawResponse: + from .resources.get_procedure import AsyncGetProcedureResourceWithRawResponse + + return AsyncGetProcedureResourceWithRawResponse(self._client.get_procedure) + + @cached_property + def delete_procedure(self) -> delete_procedure.AsyncDeleteProcedureResourceWithRawResponse: + from .resources.delete_procedure import AsyncDeleteProcedureResourceWithRawResponse + + return AsyncDeleteProcedureResourceWithRawResponse(self._client.delete_procedure) + + @cached_property + def list_procedures(self) -> list_procedures.AsyncListProceduresResourceWithRawResponse: + from .resources.list_procedures import AsyncListProceduresResourceWithRawResponse + + return AsyncListProceduresResourceWithRawResponse(self._client.list_procedures) + + @cached_property + def put_semantic_memory(self) -> put_semantic_memory.AsyncPutSemanticMemoryResourceWithRawResponse: + from .resources.put_semantic_memory import AsyncPutSemanticMemoryResourceWithRawResponse + + return AsyncPutSemanticMemoryResourceWithRawResponse(self._client.put_semantic_memory) + + @cached_property + def get_semantic_memory(self) -> get_semantic_memory.AsyncGetSemanticMemoryResourceWithRawResponse: + from .resources.get_semantic_memory import AsyncGetSemanticMemoryResourceWithRawResponse + + return AsyncGetSemanticMemoryResourceWithRawResponse(self._client.get_semantic_memory) + + @cached_property + def delete_semantic_memory(self) -> delete_semantic_memory.AsyncDeleteSemanticMemoryResourceWithRawResponse: + from .resources.delete_semantic_memory import AsyncDeleteSemanticMemoryResourceWithRawResponse + + return AsyncDeleteSemanticMemoryResourceWithRawResponse(self._client.delete_semantic_memory) + + @cached_property + def execute_query(self) -> execute_query.AsyncExecuteQueryResourceWithRawResponse: + from .resources.execute_query import AsyncExecuteQueryResourceWithRawResponse + + return AsyncExecuteQueryResourceWithRawResponse(self._client.execute_query) + + @cached_property + def get_metadata(self) -> get_metadata.AsyncGetMetadataResourceWithRawResponse: + from .resources.get_metadata import AsyncGetMetadataResourceWithRawResponse + + return AsyncGetMetadataResourceWithRawResponse(self._client.get_metadata) + + @cached_property + def update_metadata(self) -> update_metadata.AsyncUpdateMetadataResourceWithRawResponse: + from .resources.update_metadata import AsyncUpdateMetadataResourceWithRawResponse + + return AsyncUpdateMetadataResourceWithRawResponse(self._client.update_metadata) + + @cached_property + def get_pii_data(self) -> get_pii_data.AsyncGetPiiDataResourceWithRawResponse: + from .resources.get_pii_data import AsyncGetPiiDataResourceWithRawResponse + + return AsyncGetPiiDataResourceWithRawResponse(self._client.get_pii_data) + + @cached_property + def document_status(self) -> document_status.AsyncDocumentStatusResourceWithRawResponse: + from .resources.document_status import AsyncDocumentStatusResourceWithRawResponse + + return AsyncDocumentStatusResourceWithRawResponse(self._client.document_status) + + @cached_property + def document_status_bulk(self) -> document_status_bulk.AsyncDocumentStatusBulkResourceWithRawResponse: + from .resources.document_status_bulk import AsyncDocumentStatusBulkResourceWithRawResponse + + return AsyncDocumentStatusBulkResourceWithRawResponse(self._client.document_status_bulk) + + @cached_property + def rehydration_status(self) -> rehydration_status.AsyncRehydrationStatusResourceWithRawResponse: + from .resources.rehydration_status import AsyncRehydrationStatusResourceWithRawResponse + + return AsyncRehydrationStatusResourceWithRawResponse(self._client.rehydration_status) class RaindropWithStreamedResponse: + _client: Raindrop + def __init__(self, client: Raindrop) -> None: - self.query = query.QueryResourceWithStreamingResponse(client.query) - self.bucket = bucket.BucketResourceWithStreamingResponse(client.bucket) - self.put_memory = put_memory.PutMemoryResourceWithStreamingResponse(client.put_memory) - self.get_memory = get_memory.GetMemoryResourceWithStreamingResponse(client.get_memory) - self.delete_memory = delete_memory.DeleteMemoryResourceWithStreamingResponse(client.delete_memory) - self.summarize_memory = summarize_memory.SummarizeMemoryResourceWithStreamingResponse(client.summarize_memory) - self.start_session = start_session.StartSessionResourceWithStreamingResponse(client.start_session) - self.end_session = end_session.EndSessionResourceWithStreamingResponse(client.end_session) - self.rehydrate_session = rehydrate_session.RehydrateSessionResourceWithStreamingResponse( - client.rehydrate_session - ) - self.put_procedure = put_procedure.PutProcedureResourceWithStreamingResponse(client.put_procedure) - self.get_procedure = get_procedure.GetProcedureResourceWithStreamingResponse(client.get_procedure) - self.delete_procedure = delete_procedure.DeleteProcedureResourceWithStreamingResponse(client.delete_procedure) - self.list_procedures = list_procedures.ListProceduresResourceWithStreamingResponse(client.list_procedures) - self.put_semantic_memory = put_semantic_memory.PutSemanticMemoryResourceWithStreamingResponse( - client.put_semantic_memory - ) - self.get_semantic_memory = get_semantic_memory.GetSemanticMemoryResourceWithStreamingResponse( - client.get_semantic_memory - ) - self.delete_semantic_memory = delete_semantic_memory.DeleteSemanticMemoryResourceWithStreamingResponse( - client.delete_semantic_memory - ) + self._client = client + + @cached_property + def query(self) -> query.QueryResourceWithStreamingResponse: + from .resources.query import QueryResourceWithStreamingResponse + + return QueryResourceWithStreamingResponse(self._client.query) + + @cached_property + def bucket(self) -> bucket.BucketResourceWithStreamingResponse: + from .resources.bucket import BucketResourceWithStreamingResponse + + return BucketResourceWithStreamingResponse(self._client.bucket) + + @cached_property + def put_memory(self) -> put_memory.PutMemoryResourceWithStreamingResponse: + from .resources.put_memory import PutMemoryResourceWithStreamingResponse + + return PutMemoryResourceWithStreamingResponse(self._client.put_memory) + + @cached_property + def get_memory(self) -> get_memory.GetMemoryResourceWithStreamingResponse: + from .resources.get_memory import GetMemoryResourceWithStreamingResponse + + return GetMemoryResourceWithStreamingResponse(self._client.get_memory) + + @cached_property + def delete_memory(self) -> delete_memory.DeleteMemoryResourceWithStreamingResponse: + from .resources.delete_memory import DeleteMemoryResourceWithStreamingResponse + + return DeleteMemoryResourceWithStreamingResponse(self._client.delete_memory) + + @cached_property + def summarize_memory(self) -> summarize_memory.SummarizeMemoryResourceWithStreamingResponse: + from .resources.summarize_memory import SummarizeMemoryResourceWithStreamingResponse + + return SummarizeMemoryResourceWithStreamingResponse(self._client.summarize_memory) + + @cached_property + def start_session(self) -> start_session.StartSessionResourceWithStreamingResponse: + from .resources.start_session import StartSessionResourceWithStreamingResponse + + return StartSessionResourceWithStreamingResponse(self._client.start_session) + + @cached_property + def end_session(self) -> end_session.EndSessionResourceWithStreamingResponse: + from .resources.end_session import EndSessionResourceWithStreamingResponse + + return EndSessionResourceWithStreamingResponse(self._client.end_session) + + @cached_property + def rehydrate_session(self) -> rehydrate_session.RehydrateSessionResourceWithStreamingResponse: + from .resources.rehydrate_session import RehydrateSessionResourceWithStreamingResponse + + return RehydrateSessionResourceWithStreamingResponse(self._client.rehydrate_session) + + @cached_property + def put_procedure(self) -> put_procedure.PutProcedureResourceWithStreamingResponse: + from .resources.put_procedure import PutProcedureResourceWithStreamingResponse + + return PutProcedureResourceWithStreamingResponse(self._client.put_procedure) + + @cached_property + def get_procedure(self) -> get_procedure.GetProcedureResourceWithStreamingResponse: + from .resources.get_procedure import GetProcedureResourceWithStreamingResponse + + return GetProcedureResourceWithStreamingResponse(self._client.get_procedure) + + @cached_property + def delete_procedure(self) -> delete_procedure.DeleteProcedureResourceWithStreamingResponse: + from .resources.delete_procedure import DeleteProcedureResourceWithStreamingResponse + + return DeleteProcedureResourceWithStreamingResponse(self._client.delete_procedure) + + @cached_property + def list_procedures(self) -> list_procedures.ListProceduresResourceWithStreamingResponse: + from .resources.list_procedures import ListProceduresResourceWithStreamingResponse + + return ListProceduresResourceWithStreamingResponse(self._client.list_procedures) + + @cached_property + def put_semantic_memory(self) -> put_semantic_memory.PutSemanticMemoryResourceWithStreamingResponse: + from .resources.put_semantic_memory import PutSemanticMemoryResourceWithStreamingResponse + + return PutSemanticMemoryResourceWithStreamingResponse(self._client.put_semantic_memory) + + @cached_property + def get_semantic_memory(self) -> get_semantic_memory.GetSemanticMemoryResourceWithStreamingResponse: + from .resources.get_semantic_memory import GetSemanticMemoryResourceWithStreamingResponse + + return GetSemanticMemoryResourceWithStreamingResponse(self._client.get_semantic_memory) + + @cached_property + def delete_semantic_memory(self) -> delete_semantic_memory.DeleteSemanticMemoryResourceWithStreamingResponse: + from .resources.delete_semantic_memory import DeleteSemanticMemoryResourceWithStreamingResponse + + return DeleteSemanticMemoryResourceWithStreamingResponse(self._client.delete_semantic_memory) + + @cached_property + def execute_query(self) -> execute_query.ExecuteQueryResourceWithStreamingResponse: + from .resources.execute_query import ExecuteQueryResourceWithStreamingResponse + + return ExecuteQueryResourceWithStreamingResponse(self._client.execute_query) + + @cached_property + def get_metadata(self) -> get_metadata.GetMetadataResourceWithStreamingResponse: + from .resources.get_metadata import GetMetadataResourceWithStreamingResponse + + return GetMetadataResourceWithStreamingResponse(self._client.get_metadata) + + @cached_property + def update_metadata(self) -> update_metadata.UpdateMetadataResourceWithStreamingResponse: + from .resources.update_metadata import UpdateMetadataResourceWithStreamingResponse + + return UpdateMetadataResourceWithStreamingResponse(self._client.update_metadata) + + @cached_property + def get_pii_data(self) -> get_pii_data.GetPiiDataResourceWithStreamingResponse: + from .resources.get_pii_data import GetPiiDataResourceWithStreamingResponse + + return GetPiiDataResourceWithStreamingResponse(self._client.get_pii_data) + + @cached_property + def document_status(self) -> document_status.DocumentStatusResourceWithStreamingResponse: + from .resources.document_status import DocumentStatusResourceWithStreamingResponse + + return DocumentStatusResourceWithStreamingResponse(self._client.document_status) + + @cached_property + def document_status_bulk(self) -> document_status_bulk.DocumentStatusBulkResourceWithStreamingResponse: + from .resources.document_status_bulk import DocumentStatusBulkResourceWithStreamingResponse + + return DocumentStatusBulkResourceWithStreamingResponse(self._client.document_status_bulk) + + @cached_property + def rehydration_status(self) -> rehydration_status.RehydrationStatusResourceWithStreamingResponse: + from .resources.rehydration_status import RehydrationStatusResourceWithStreamingResponse + + return RehydrationStatusResourceWithStreamingResponse(self._client.rehydration_status) class AsyncRaindropWithStreamedResponse: + _client: AsyncRaindrop + def __init__(self, client: AsyncRaindrop) -> None: - self.query = query.AsyncQueryResourceWithStreamingResponse(client.query) - self.bucket = bucket.AsyncBucketResourceWithStreamingResponse(client.bucket) - self.put_memory = put_memory.AsyncPutMemoryResourceWithStreamingResponse(client.put_memory) - self.get_memory = get_memory.AsyncGetMemoryResourceWithStreamingResponse(client.get_memory) - self.delete_memory = delete_memory.AsyncDeleteMemoryResourceWithStreamingResponse(client.delete_memory) - self.summarize_memory = summarize_memory.AsyncSummarizeMemoryResourceWithStreamingResponse( - client.summarize_memory - ) - self.start_session = start_session.AsyncStartSessionResourceWithStreamingResponse(client.start_session) - self.end_session = end_session.AsyncEndSessionResourceWithStreamingResponse(client.end_session) - self.rehydrate_session = rehydrate_session.AsyncRehydrateSessionResourceWithStreamingResponse( - client.rehydrate_session - ) - self.put_procedure = put_procedure.AsyncPutProcedureResourceWithStreamingResponse(client.put_procedure) - self.get_procedure = get_procedure.AsyncGetProcedureResourceWithStreamingResponse(client.get_procedure) - self.delete_procedure = delete_procedure.AsyncDeleteProcedureResourceWithStreamingResponse( - client.delete_procedure - ) - self.list_procedures = list_procedures.AsyncListProceduresResourceWithStreamingResponse(client.list_procedures) - self.put_semantic_memory = put_semantic_memory.AsyncPutSemanticMemoryResourceWithStreamingResponse( - client.put_semantic_memory - ) - self.get_semantic_memory = get_semantic_memory.AsyncGetSemanticMemoryResourceWithStreamingResponse( - client.get_semantic_memory - ) - self.delete_semantic_memory = delete_semantic_memory.AsyncDeleteSemanticMemoryResourceWithStreamingResponse( - client.delete_semantic_memory - ) + self._client = client + + @cached_property + def query(self) -> query.AsyncQueryResourceWithStreamingResponse: + from .resources.query import AsyncQueryResourceWithStreamingResponse + + return AsyncQueryResourceWithStreamingResponse(self._client.query) + + @cached_property + def bucket(self) -> bucket.AsyncBucketResourceWithStreamingResponse: + from .resources.bucket import AsyncBucketResourceWithStreamingResponse + + return AsyncBucketResourceWithStreamingResponse(self._client.bucket) + + @cached_property + def put_memory(self) -> put_memory.AsyncPutMemoryResourceWithStreamingResponse: + from .resources.put_memory import AsyncPutMemoryResourceWithStreamingResponse + + return AsyncPutMemoryResourceWithStreamingResponse(self._client.put_memory) + + @cached_property + def get_memory(self) -> get_memory.AsyncGetMemoryResourceWithStreamingResponse: + from .resources.get_memory import AsyncGetMemoryResourceWithStreamingResponse + + return AsyncGetMemoryResourceWithStreamingResponse(self._client.get_memory) + + @cached_property + def delete_memory(self) -> delete_memory.AsyncDeleteMemoryResourceWithStreamingResponse: + from .resources.delete_memory import AsyncDeleteMemoryResourceWithStreamingResponse + + return AsyncDeleteMemoryResourceWithStreamingResponse(self._client.delete_memory) + + @cached_property + def summarize_memory(self) -> summarize_memory.AsyncSummarizeMemoryResourceWithStreamingResponse: + from .resources.summarize_memory import AsyncSummarizeMemoryResourceWithStreamingResponse + + return AsyncSummarizeMemoryResourceWithStreamingResponse(self._client.summarize_memory) + + @cached_property + def start_session(self) -> start_session.AsyncStartSessionResourceWithStreamingResponse: + from .resources.start_session import AsyncStartSessionResourceWithStreamingResponse + + return AsyncStartSessionResourceWithStreamingResponse(self._client.start_session) + + @cached_property + def end_session(self) -> end_session.AsyncEndSessionResourceWithStreamingResponse: + from .resources.end_session import AsyncEndSessionResourceWithStreamingResponse + + return AsyncEndSessionResourceWithStreamingResponse(self._client.end_session) + + @cached_property + def rehydrate_session(self) -> rehydrate_session.AsyncRehydrateSessionResourceWithStreamingResponse: + from .resources.rehydrate_session import AsyncRehydrateSessionResourceWithStreamingResponse + + return AsyncRehydrateSessionResourceWithStreamingResponse(self._client.rehydrate_session) + + @cached_property + def put_procedure(self) -> put_procedure.AsyncPutProcedureResourceWithStreamingResponse: + from .resources.put_procedure import AsyncPutProcedureResourceWithStreamingResponse + + return AsyncPutProcedureResourceWithStreamingResponse(self._client.put_procedure) + + @cached_property + def get_procedure(self) -> get_procedure.AsyncGetProcedureResourceWithStreamingResponse: + from .resources.get_procedure import AsyncGetProcedureResourceWithStreamingResponse + + return AsyncGetProcedureResourceWithStreamingResponse(self._client.get_procedure) + + @cached_property + def delete_procedure(self) -> delete_procedure.AsyncDeleteProcedureResourceWithStreamingResponse: + from .resources.delete_procedure import AsyncDeleteProcedureResourceWithStreamingResponse + + return AsyncDeleteProcedureResourceWithStreamingResponse(self._client.delete_procedure) + + @cached_property + def list_procedures(self) -> list_procedures.AsyncListProceduresResourceWithStreamingResponse: + from .resources.list_procedures import AsyncListProceduresResourceWithStreamingResponse + + return AsyncListProceduresResourceWithStreamingResponse(self._client.list_procedures) + + @cached_property + def put_semantic_memory(self) -> put_semantic_memory.AsyncPutSemanticMemoryResourceWithStreamingResponse: + from .resources.put_semantic_memory import AsyncPutSemanticMemoryResourceWithStreamingResponse + + return AsyncPutSemanticMemoryResourceWithStreamingResponse(self._client.put_semantic_memory) + + @cached_property + def get_semantic_memory(self) -> get_semantic_memory.AsyncGetSemanticMemoryResourceWithStreamingResponse: + from .resources.get_semantic_memory import AsyncGetSemanticMemoryResourceWithStreamingResponse + + return AsyncGetSemanticMemoryResourceWithStreamingResponse(self._client.get_semantic_memory) + + @cached_property + def delete_semantic_memory(self) -> delete_semantic_memory.AsyncDeleteSemanticMemoryResourceWithStreamingResponse: + from .resources.delete_semantic_memory import AsyncDeleteSemanticMemoryResourceWithStreamingResponse + + return AsyncDeleteSemanticMemoryResourceWithStreamingResponse(self._client.delete_semantic_memory) + + @cached_property + def execute_query(self) -> execute_query.AsyncExecuteQueryResourceWithStreamingResponse: + from .resources.execute_query import AsyncExecuteQueryResourceWithStreamingResponse + + return AsyncExecuteQueryResourceWithStreamingResponse(self._client.execute_query) + + @cached_property + def get_metadata(self) -> get_metadata.AsyncGetMetadataResourceWithStreamingResponse: + from .resources.get_metadata import AsyncGetMetadataResourceWithStreamingResponse + + return AsyncGetMetadataResourceWithStreamingResponse(self._client.get_metadata) + + @cached_property + def update_metadata(self) -> update_metadata.AsyncUpdateMetadataResourceWithStreamingResponse: + from .resources.update_metadata import AsyncUpdateMetadataResourceWithStreamingResponse + + return AsyncUpdateMetadataResourceWithStreamingResponse(self._client.update_metadata) + + @cached_property + def get_pii_data(self) -> get_pii_data.AsyncGetPiiDataResourceWithStreamingResponse: + from .resources.get_pii_data import AsyncGetPiiDataResourceWithStreamingResponse + + return AsyncGetPiiDataResourceWithStreamingResponse(self._client.get_pii_data) + + @cached_property + def document_status(self) -> document_status.AsyncDocumentStatusResourceWithStreamingResponse: + from .resources.document_status import AsyncDocumentStatusResourceWithStreamingResponse + + return AsyncDocumentStatusResourceWithStreamingResponse(self._client.document_status) + + @cached_property + def document_status_bulk(self) -> document_status_bulk.AsyncDocumentStatusBulkResourceWithStreamingResponse: + from .resources.document_status_bulk import AsyncDocumentStatusBulkResourceWithStreamingResponse + + return AsyncDocumentStatusBulkResourceWithStreamingResponse(self._client.document_status_bulk) + + @cached_property + def rehydration_status(self) -> rehydration_status.AsyncRehydrationStatusResourceWithStreamingResponse: + from .resources.rehydration_status import AsyncRehydrationStatusResourceWithStreamingResponse + + return AsyncRehydrationStatusResourceWithStreamingResponse(self._client.rehydration_status) Client = Raindrop diff --git a/src/raindrop/_compat.py b/src/raindrop/_compat.py index bdef67f0..786ff42a 100644 --- a/src/raindrop/_compat.py +++ b/src/raindrop/_compat.py @@ -139,6 +139,7 @@ def model_dump( exclude_defaults: bool = False, warnings: bool = True, mode: Literal["json", "python"] = "python", + by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( @@ -148,13 +149,12 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, + by_alias=by_alias, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) ), ) diff --git a/src/raindrop/_models.py b/src/raindrop/_models.py index 6a3cd1d2..29070e05 100644 --- a/src/raindrop/_models.py +++ b/src/raindrop/_models.py @@ -2,7 +2,21 @@ import os import inspect -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +import weakref +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) from datetime import date, datetime from typing_extensions import ( List, @@ -256,15 +270,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -272,16 +287,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -298,6 +321,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -314,15 +339,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -354,6 +381,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, @@ -573,6 +604,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -615,8 +649,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -669,7 +704,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details @@ -765,6 +800,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] json_data: Body extra_json: AnyMapping follow_redirects: bool @@ -783,6 +819,7 @@ class FinalRequestOptions(pydantic.BaseModel): post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None diff --git a/src/raindrop/_streaming.py b/src/raindrop/_streaming.py index 02de1892..a37099c4 100644 --- a/src/raindrop/_streaming.py +++ b/src/raindrop/_streaming.py @@ -54,12 +54,12 @@ def __stream__(self) -> Iterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - for _sse in iterator: - ... + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() def __enter__(self) -> Self: return self @@ -118,12 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - async for _sse in iterator: - ... + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() async def __aenter__(self) -> Self: return self diff --git a/src/raindrop/_types.py b/src/raindrop/_types.py index c367fa55..1f5e7e52 100644 --- a/src/raindrop/_types.py +++ b/src/raindrop/_types.py @@ -13,9 +13,11 @@ Mapping, TypeVar, Callable, + Iterable, Iterator, Optional, Sequence, + AsyncIterable, ) from typing_extensions import ( Set, @@ -56,6 +58,13 @@ else: Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + FileTypes = Union[ # file (or bytes) FileContent, @@ -243,6 +252,9 @@ class HttpxSendArgs(TypedDict, total=False): if TYPE_CHECKING: # This works because str.__contains__ does not accept object (either in typeshed or at runtime) # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. class SequenceNotStr(Protocol[_T_co]): @overload def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... @@ -251,8 +263,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T_co]: ... - def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... - def count(self, value: Any, /) -> int: ... def __reversed__(self) -> Iterator[_T_co]: ... else: # just point this to a normal `Sequence` at runtime to avoid having to special case diff --git a/src/raindrop/_utils/_compat.py b/src/raindrop/_utils/_compat.py index dd703233..2c70b299 100644 --- a/src/raindrop/_utils/_compat.py +++ b/src/raindrop/_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: diff --git a/src/raindrop/_utils/_json.py b/src/raindrop/_utils/_json.py new file mode 100644 index 00000000..60584214 --- /dev/null +++ b/src/raindrop/_utils/_json.py @@ -0,0 +1,35 @@ +import json +from typing import Any +from datetime import datetime +from typing_extensions import override + +import pydantic + +from .._compat import model_dump + + +def openapi_dumps(obj: Any) -> bytes: + """ + Serialize an object to UTF-8 encoded JSON bytes. + + Extends the standard json.dumps with support for additional types + commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. + """ + return json.dumps( + obj, + cls=_CustomEncoder, + # Uses the same defaults as httpx's JSON serialization + ensure_ascii=False, + separators=(",", ":"), + allow_nan=False, + ).encode() + + +class _CustomEncoder(json.JSONEncoder): + @override + def default(self, o: Any) -> Any: + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, pydantic.BaseModel): + return model_dump(o, exclude_unset=True, mode="json", by_alias=True) + return super().default(o) diff --git a/src/raindrop/_utils/_sync.py b/src/raindrop/_utils/_sync.py index ad7ec71b..f6027c18 100644 --- a/src/raindrop/_utils/_sync.py +++ b/src/raindrop/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: diff --git a/src/raindrop/_utils/_utils.py b/src/raindrop/_utils/_utils.py index 50d59269..eec7f4a1 100644 --- a/src/raindrop/_utils/_utils.py +++ b/src/raindrop/_utils/_utils.py @@ -133,7 +133,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. +# care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input diff --git a/src/raindrop/_version.py b/src/raindrop/_version.py index c184c3c7..97d09ba5 100644 --- a/src/raindrop/_version.py +++ b/src/raindrop/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "raindrop" -__version__ = "0.6.43" # x-release-please-version +__version__ = "0.17.0" # x-release-please-version diff --git a/src/raindrop/resources/__init__.py b/src/raindrop/resources/__init__.py index 1b5ed6fb..5a9c06e5 100644 --- a/src/raindrop/resources/__init__.py +++ b/src/raindrop/resources/__init__.py @@ -40,6 +40,22 @@ EndSessionResourceWithStreamingResponse, AsyncEndSessionResourceWithStreamingResponse, ) +from .get_metadata import ( + GetMetadataResource, + AsyncGetMetadataResource, + GetMetadataResourceWithRawResponse, + AsyncGetMetadataResourceWithRawResponse, + GetMetadataResourceWithStreamingResponse, + AsyncGetMetadataResourceWithStreamingResponse, +) +from .get_pii_data import ( + GetPiiDataResource, + AsyncGetPiiDataResource, + GetPiiDataResourceWithRawResponse, + AsyncGetPiiDataResourceWithRawResponse, + GetPiiDataResourceWithStreamingResponse, + AsyncGetPiiDataResourceWithStreamingResponse, +) from .delete_memory import ( DeleteMemoryResource, AsyncDeleteMemoryResource, @@ -48,6 +64,14 @@ DeleteMemoryResourceWithStreamingResponse, AsyncDeleteMemoryResourceWithStreamingResponse, ) +from .execute_query import ( + ExecuteQueryResource, + AsyncExecuteQueryResource, + ExecuteQueryResourceWithRawResponse, + AsyncExecuteQueryResourceWithRawResponse, + ExecuteQueryResourceWithStreamingResponse, + AsyncExecuteQueryResourceWithStreamingResponse, +) from .get_procedure import ( GetProcedureResource, AsyncGetProcedureResource, @@ -72,6 +96,14 @@ StartSessionResourceWithStreamingResponse, AsyncStartSessionResourceWithStreamingResponse, ) +from .document_status import ( + DocumentStatusResource, + AsyncDocumentStatusResource, + DocumentStatusResourceWithRawResponse, + AsyncDocumentStatusResourceWithRawResponse, + DocumentStatusResourceWithStreamingResponse, + AsyncDocumentStatusResourceWithStreamingResponse, +) from .list_procedures import ( ListProceduresResource, AsyncListProceduresResource, @@ -80,6 +112,14 @@ ListProceduresResourceWithStreamingResponse, AsyncListProceduresResourceWithStreamingResponse, ) +from .update_metadata import ( + UpdateMetadataResource, + AsyncUpdateMetadataResource, + UpdateMetadataResourceWithRawResponse, + AsyncUpdateMetadataResourceWithRawResponse, + UpdateMetadataResourceWithStreamingResponse, + AsyncUpdateMetadataResourceWithStreamingResponse, +) from .delete_procedure import ( DeleteProcedureResource, AsyncDeleteProcedureResource, @@ -104,6 +144,14 @@ RehydrateSessionResourceWithStreamingResponse, AsyncRehydrateSessionResourceWithStreamingResponse, ) +from .rehydration_status import ( + RehydrationStatusResource, + AsyncRehydrationStatusResource, + RehydrationStatusResourceWithRawResponse, + AsyncRehydrationStatusResourceWithRawResponse, + RehydrationStatusResourceWithStreamingResponse, + AsyncRehydrationStatusResourceWithStreamingResponse, +) from .get_semantic_memory import ( GetSemanticMemoryResource, AsyncGetSemanticMemoryResource, @@ -120,6 +168,14 @@ PutSemanticMemoryResourceWithStreamingResponse, AsyncPutSemanticMemoryResourceWithStreamingResponse, ) +from .document_status_bulk import ( + DocumentStatusBulkResource, + AsyncDocumentStatusBulkResource, + DocumentStatusBulkResourceWithRawResponse, + AsyncDocumentStatusBulkResourceWithRawResponse, + DocumentStatusBulkResourceWithStreamingResponse, + AsyncDocumentStatusBulkResourceWithStreamingResponse, +) from .delete_semantic_memory import ( DeleteSemanticMemoryResource, AsyncDeleteSemanticMemoryResource, @@ -226,4 +282,46 @@ "AsyncDeleteSemanticMemoryResourceWithRawResponse", "DeleteSemanticMemoryResourceWithStreamingResponse", "AsyncDeleteSemanticMemoryResourceWithStreamingResponse", + "ExecuteQueryResource", + "AsyncExecuteQueryResource", + "ExecuteQueryResourceWithRawResponse", + "AsyncExecuteQueryResourceWithRawResponse", + "ExecuteQueryResourceWithStreamingResponse", + "AsyncExecuteQueryResourceWithStreamingResponse", + "GetMetadataResource", + "AsyncGetMetadataResource", + "GetMetadataResourceWithRawResponse", + "AsyncGetMetadataResourceWithRawResponse", + "GetMetadataResourceWithStreamingResponse", + "AsyncGetMetadataResourceWithStreamingResponse", + "UpdateMetadataResource", + "AsyncUpdateMetadataResource", + "UpdateMetadataResourceWithRawResponse", + "AsyncUpdateMetadataResourceWithRawResponse", + "UpdateMetadataResourceWithStreamingResponse", + "AsyncUpdateMetadataResourceWithStreamingResponse", + "GetPiiDataResource", + "AsyncGetPiiDataResource", + "GetPiiDataResourceWithRawResponse", + "AsyncGetPiiDataResourceWithRawResponse", + "GetPiiDataResourceWithStreamingResponse", + "AsyncGetPiiDataResourceWithStreamingResponse", + "DocumentStatusResource", + "AsyncDocumentStatusResource", + "DocumentStatusResourceWithRawResponse", + "AsyncDocumentStatusResourceWithRawResponse", + "DocumentStatusResourceWithStreamingResponse", + "AsyncDocumentStatusResourceWithStreamingResponse", + "DocumentStatusBulkResource", + "AsyncDocumentStatusBulkResource", + "DocumentStatusBulkResourceWithRawResponse", + "AsyncDocumentStatusBulkResourceWithRawResponse", + "DocumentStatusBulkResourceWithStreamingResponse", + "AsyncDocumentStatusBulkResourceWithStreamingResponse", + "RehydrationStatusResource", + "AsyncRehydrationStatusResource", + "RehydrationStatusResourceWithRawResponse", + "AsyncRehydrationStatusResourceWithRawResponse", + "RehydrationStatusResourceWithStreamingResponse", + "AsyncRehydrationStatusResourceWithStreamingResponse", ] diff --git a/src/raindrop/resources/bucket/__init__.py b/src/raindrop/resources/bucket/__init__.py new file mode 100644 index 00000000..de1850ec --- /dev/null +++ b/src/raindrop/resources/bucket/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .bucket import ( + BucketResource, + AsyncBucketResource, + BucketResourceWithRawResponse, + AsyncBucketResourceWithRawResponse, + BucketResourceWithStreamingResponse, + AsyncBucketResourceWithStreamingResponse, +) +from .by_status import ( + ByStatusResource, + AsyncByStatusResource, + ByStatusResourceWithRawResponse, + AsyncByStatusResourceWithRawResponse, + ByStatusResourceWithStreamingResponse, + AsyncByStatusResourceWithStreamingResponse, +) + +__all__ = [ + "ByStatusResource", + "AsyncByStatusResource", + "ByStatusResourceWithRawResponse", + "AsyncByStatusResourceWithRawResponse", + "ByStatusResourceWithStreamingResponse", + "AsyncByStatusResourceWithStreamingResponse", + "BucketResource", + "AsyncBucketResource", + "BucketResourceWithRawResponse", + "AsyncBucketResourceWithRawResponse", + "BucketResourceWithStreamingResponse", + "AsyncBucketResourceWithStreamingResponse", +] diff --git a/src/raindrop/resources/bucket.py b/src/raindrop/resources/bucket/bucket.py similarity index 87% rename from src/raindrop/resources/bucket.py rename to src/raindrop/resources/bucket/bucket.py index aa561cd9..290ba76c 100644 --- a/src/raindrop/resources/bucket.py +++ b/src/raindrop/resources/bucket/bucket.py @@ -2,31 +2,43 @@ from __future__ import annotations -from typing import Union +from typing import Union, Optional import httpx -from ..types import bucket_get_params, bucket_put_params, bucket_list_params, bucket_delete_params -from .._types import Body, Query, Headers, NotGiven, Base64FileInput, 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 bucket_get_params, bucket_put_params, bucket_list_params, bucket_delete_params +from ..._types import Body, Omit, Query, Headers, NotGiven, Base64FileInput, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from .by_status import ( + ByStatusResource, + AsyncByStatusResource, + ByStatusResourceWithRawResponse, + AsyncByStatusResourceWithRawResponse, + ByStatusResourceWithStreamingResponse, + AsyncByStatusResourceWithStreamingResponse, +) +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.bucket_get_response import BucketGetResponse -from ..types.bucket_put_response import BucketPutResponse -from ..types.bucket_list_response import BucketListResponse -from ..types.bucket_locator_param import BucketLocatorParam +from ..._base_client import make_request_options +from ...types.bucket_get_response import BucketGetResponse +from ...types.bucket_put_response import BucketPutResponse +from ...types.bucket_list_response import BucketListResponse +from ...types.bucket_locator_param import BucketLocatorParam __all__ = ["BucketResource", "AsyncBucketResource"] class BucketResource(SyncAPIResource): + @cached_property + def by_status(self) -> ByStatusResource: + return ByStatusResource(self._client) + @cached_property def with_raw_response(self) -> BucketResourceWithRawResponse: """ @@ -50,6 +62,7 @@ def list( self, *, bucket_location: BucketLocatorParam, + prefix: Optional[str] | 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, @@ -66,6 +79,9 @@ def list( bucket_location: The buckets to search. If provided, the search will only return results from these buckets + prefix: Optional prefix to filter object keys (e.g., "documents/" to only list objects + in documents folder) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -76,7 +92,13 @@ def list( """ return self._post( "/v1/list_objects", - body=maybe_transform({"bucket_location": bucket_location}, bucket_list_params.BucketListParams), + body=maybe_transform( + { + "bucket_location": bucket_location, + "prefix": prefix, + }, + bucket_list_params.BucketListParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -234,6 +256,10 @@ def put( class AsyncBucketResource(AsyncAPIResource): + @cached_property + def by_status(self) -> AsyncByStatusResource: + return AsyncByStatusResource(self._client) + @cached_property def with_raw_response(self) -> AsyncBucketResourceWithRawResponse: """ @@ -257,6 +283,7 @@ async def list( self, *, bucket_location: BucketLocatorParam, + prefix: Optional[str] | 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, @@ -273,6 +300,9 @@ async def list( bucket_location: The buckets to search. If provided, the search will only return results from these buckets + prefix: Optional prefix to filter object keys (e.g., "documents/" to only list objects + in documents folder) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -283,7 +313,13 @@ async def list( """ return await self._post( "/v1/list_objects", - body=await async_maybe_transform({"bucket_location": bucket_location}, bucket_list_params.BucketListParams), + body=await async_maybe_transform( + { + "bucket_location": bucket_location, + "prefix": prefix, + }, + bucket_list_params.BucketListParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -457,6 +493,10 @@ def __init__(self, bucket: BucketResource) -> None: bucket.put, ) + @cached_property + def by_status(self) -> ByStatusResourceWithRawResponse: + return ByStatusResourceWithRawResponse(self._bucket.by_status) + class AsyncBucketResourceWithRawResponse: def __init__(self, bucket: AsyncBucketResource) -> None: @@ -475,6 +515,10 @@ def __init__(self, bucket: AsyncBucketResource) -> None: bucket.put, ) + @cached_property + def by_status(self) -> AsyncByStatusResourceWithRawResponse: + return AsyncByStatusResourceWithRawResponse(self._bucket.by_status) + class BucketResourceWithStreamingResponse: def __init__(self, bucket: BucketResource) -> None: @@ -493,6 +537,10 @@ def __init__(self, bucket: BucketResource) -> None: bucket.put, ) + @cached_property + def by_status(self) -> ByStatusResourceWithStreamingResponse: + return ByStatusResourceWithStreamingResponse(self._bucket.by_status) + class AsyncBucketResourceWithStreamingResponse: def __init__(self, bucket: AsyncBucketResource) -> None: @@ -510,3 +558,7 @@ def __init__(self, bucket: AsyncBucketResource) -> None: self.put = async_to_streamed_response_wrapper( bucket.put, ) + + @cached_property + def by_status(self) -> AsyncByStatusResourceWithStreamingResponse: + return AsyncByStatusResourceWithStreamingResponse(self._bucket.by_status) diff --git a/src/raindrop/resources/bucket/by_status.py b/src/raindrop/resources/bucket/by_status.py new file mode 100644 index 00000000..dd4b9606 --- /dev/null +++ b/src/raindrop/resources/bucket/by_status.py @@ -0,0 +1,226 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, 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 ..._base_client import make_request_options +from ...types.bucket import by_status_list_objects_params +from ...types.bucket_locator_param import BucketLocatorParam +from ...types.bucket.by_status_list_objects_response import ByStatusListObjectsResponse + +__all__ = ["ByStatusResource", "AsyncByStatusResource"] + + +class ByStatusResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ByStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return ByStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ByStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return ByStatusResourceWithStreamingResponse(self) + + def list_objects( + self, + *, + bucket_location: BucketLocatorParam, + statuses: SequenceNotStr[str], + exclude: Optional[bool] | Omit = omit, + partition: Optional[str] | Omit = omit, + prefix: Optional[str] | 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, + ) -> ByStatusListObjectsResponse: + """List objects filtered by indexing status. + + Efficiently queries document storage + across all shards to find objects matching (or excluding) specified statuses. + Useful for identifying objects that need attention (e.g., failed, processing) or + tracking indexing progress. + + Args: + bucket_location: The storage bucket to query + + statuses: Status values to filter by (e.g., "completed", "failed", "processing", + "ingesting", "partial", "uploading", "not_found") + + exclude: If true, returns objects NOT matching the specified statuses (inverts the + filter) + + partition: Partition to query (defaults to "default") + + prefix: Optional prefix to filter object keys (e.g., "documents/" to only search in + documents folder) + + 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/list_objects_by_status", + body=maybe_transform( + { + "bucket_location": bucket_location, + "statuses": statuses, + "exclude": exclude, + "partition": partition, + "prefix": prefix, + }, + by_status_list_objects_params.ByStatusListObjectsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ByStatusListObjectsResponse, + ) + + +class AsyncByStatusResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncByStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncByStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncByStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncByStatusResourceWithStreamingResponse(self) + + async def list_objects( + self, + *, + bucket_location: BucketLocatorParam, + statuses: SequenceNotStr[str], + exclude: Optional[bool] | Omit = omit, + partition: Optional[str] | Omit = omit, + prefix: Optional[str] | 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, + ) -> ByStatusListObjectsResponse: + """List objects filtered by indexing status. + + Efficiently queries document storage + across all shards to find objects matching (or excluding) specified statuses. + Useful for identifying objects that need attention (e.g., failed, processing) or + tracking indexing progress. + + Args: + bucket_location: The storage bucket to query + + statuses: Status values to filter by (e.g., "completed", "failed", "processing", + "ingesting", "partial", "uploading", "not_found") + + exclude: If true, returns objects NOT matching the specified statuses (inverts the + filter) + + partition: Partition to query (defaults to "default") + + prefix: Optional prefix to filter object keys (e.g., "documents/" to only search in + documents folder) + + 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/list_objects_by_status", + body=await async_maybe_transform( + { + "bucket_location": bucket_location, + "statuses": statuses, + "exclude": exclude, + "partition": partition, + "prefix": prefix, + }, + by_status_list_objects_params.ByStatusListObjectsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ByStatusListObjectsResponse, + ) + + +class ByStatusResourceWithRawResponse: + def __init__(self, by_status: ByStatusResource) -> None: + self._by_status = by_status + + self.list_objects = to_raw_response_wrapper( + by_status.list_objects, + ) + + +class AsyncByStatusResourceWithRawResponse: + def __init__(self, by_status: AsyncByStatusResource) -> None: + self._by_status = by_status + + self.list_objects = async_to_raw_response_wrapper( + by_status.list_objects, + ) + + +class ByStatusResourceWithStreamingResponse: + def __init__(self, by_status: ByStatusResource) -> None: + self._by_status = by_status + + self.list_objects = to_streamed_response_wrapper( + by_status.list_objects, + ) + + +class AsyncByStatusResourceWithStreamingResponse: + def __init__(self, by_status: AsyncByStatusResource) -> None: + self._by_status = by_status + + self.list_objects = async_to_streamed_response_wrapper( + by_status.list_objects, + ) diff --git a/src/raindrop/resources/document_status.py b/src/raindrop/resources/document_status.py new file mode 100644 index 00000000..37a049ac --- /dev/null +++ b/src/raindrop/resources/document_status.py @@ -0,0 +1,204 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import document_status_get_status_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 +from ..types.bucket_locator_param import BucketLocatorParam +from ..types.document_status_get_status_response import DocumentStatusGetStatusResponse + +__all__ = ["DocumentStatusResource", "AsyncDocumentStatusResource"] + + +class DocumentStatusResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DocumentStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return DocumentStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return DocumentStatusResourceWithStreamingResponse(self) + + def get_status( + self, + *, + bucket_location: BucketLocatorParam, + object_id: str, + partition: Optional[str] | 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, + ) -> DocumentStatusGetStatusResponse: + """Get the indexing status of a document by its object key. + + This endpoint returns + the current indexing status of a document including progress through various + processing stages. + + Args: + bucket_location: The storage bucket containing the target document + + object_id: Document identifier within the bucket (object key) + + partition: Optional partition identifier for multi-tenant data isolation. Defaults to + 'default' if not specified + + 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/document_status", + body=maybe_transform( + { + "bucket_location": bucket_location, + "object_id": object_id, + "partition": partition, + }, + document_status_get_status_params.DocumentStatusGetStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentStatusGetStatusResponse, + ) + + +class AsyncDocumentStatusResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDocumentStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncDocumentStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncDocumentStatusResourceWithStreamingResponse(self) + + async def get_status( + self, + *, + bucket_location: BucketLocatorParam, + object_id: str, + partition: Optional[str] | 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, + ) -> DocumentStatusGetStatusResponse: + """Get the indexing status of a document by its object key. + + This endpoint returns + the current indexing status of a document including progress through various + processing stages. + + Args: + bucket_location: The storage bucket containing the target document + + object_id: Document identifier within the bucket (object key) + + partition: Optional partition identifier for multi-tenant data isolation. Defaults to + 'default' if not specified + + 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/document_status", + body=await async_maybe_transform( + { + "bucket_location": bucket_location, + "object_id": object_id, + "partition": partition, + }, + document_status_get_status_params.DocumentStatusGetStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentStatusGetStatusResponse, + ) + + +class DocumentStatusResourceWithRawResponse: + def __init__(self, document_status: DocumentStatusResource) -> None: + self._document_status = document_status + + self.get_status = to_raw_response_wrapper( + document_status.get_status, + ) + + +class AsyncDocumentStatusResourceWithRawResponse: + def __init__(self, document_status: AsyncDocumentStatusResource) -> None: + self._document_status = document_status + + self.get_status = async_to_raw_response_wrapper( + document_status.get_status, + ) + + +class DocumentStatusResourceWithStreamingResponse: + def __init__(self, document_status: DocumentStatusResource) -> None: + self._document_status = document_status + + self.get_status = to_streamed_response_wrapper( + document_status.get_status, + ) + + +class AsyncDocumentStatusResourceWithStreamingResponse: + def __init__(self, document_status: AsyncDocumentStatusResource) -> None: + self._document_status = document_status + + self.get_status = async_to_streamed_response_wrapper( + document_status.get_status, + ) diff --git a/src/raindrop/resources/document_status_bulk.py b/src/raindrop/resources/document_status_bulk.py new file mode 100644 index 00000000..3d538162 --- /dev/null +++ b/src/raindrop/resources/document_status_bulk.py @@ -0,0 +1,204 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import document_status_bulk_get_status_bulk_params +from .._types import Body, Omit, Query, Headers, NotGiven, 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 .._base_client import make_request_options +from ..types.bucket_locator_param import BucketLocatorParam +from ..types.document_status_bulk_get_status_bulk_response import DocumentStatusBulkGetStatusBulkResponse + +__all__ = ["DocumentStatusBulkResource", "AsyncDocumentStatusBulkResource"] + + +class DocumentStatusBulkResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DocumentStatusBulkResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return DocumentStatusBulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentStatusBulkResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return DocumentStatusBulkResourceWithStreamingResponse(self) + + def get_status_bulk( + self, + *, + bucket_location: BucketLocatorParam, + object_ids: SequenceNotStr[str], + partition: Optional[str] | 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, + ) -> DocumentStatusBulkGetStatusBulkResponse: + """Get the indexing status for multiple documents in a single request. + + This is + significantly more efficient than making individual GetDocumentStatus calls, as + it searches shards once and returns status for all requested documents. + + Args: + bucket_location: The storage bucket containing the target documents + + object_ids: List of document identifiers (object keys) to get status for + + partition: Optional partition identifier for multi-tenant data isolation. Defaults to + 'default' if not specified + + 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/document_status_bulk", + body=maybe_transform( + { + "bucket_location": bucket_location, + "object_ids": object_ids, + "partition": partition, + }, + document_status_bulk_get_status_bulk_params.DocumentStatusBulkGetStatusBulkParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentStatusBulkGetStatusBulkResponse, + ) + + +class AsyncDocumentStatusBulkResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDocumentStatusBulkResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncDocumentStatusBulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentStatusBulkResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncDocumentStatusBulkResourceWithStreamingResponse(self) + + async def get_status_bulk( + self, + *, + bucket_location: BucketLocatorParam, + object_ids: SequenceNotStr[str], + partition: Optional[str] | 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, + ) -> DocumentStatusBulkGetStatusBulkResponse: + """Get the indexing status for multiple documents in a single request. + + This is + significantly more efficient than making individual GetDocumentStatus calls, as + it searches shards once and returns status for all requested documents. + + Args: + bucket_location: The storage bucket containing the target documents + + object_ids: List of document identifiers (object keys) to get status for + + partition: Optional partition identifier for multi-tenant data isolation. Defaults to + 'default' if not specified + + 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/document_status_bulk", + body=await async_maybe_transform( + { + "bucket_location": bucket_location, + "object_ids": object_ids, + "partition": partition, + }, + document_status_bulk_get_status_bulk_params.DocumentStatusBulkGetStatusBulkParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentStatusBulkGetStatusBulkResponse, + ) + + +class DocumentStatusBulkResourceWithRawResponse: + def __init__(self, document_status_bulk: DocumentStatusBulkResource) -> None: + self._document_status_bulk = document_status_bulk + + self.get_status_bulk = to_raw_response_wrapper( + document_status_bulk.get_status_bulk, + ) + + +class AsyncDocumentStatusBulkResourceWithRawResponse: + def __init__(self, document_status_bulk: AsyncDocumentStatusBulkResource) -> None: + self._document_status_bulk = document_status_bulk + + self.get_status_bulk = async_to_raw_response_wrapper( + document_status_bulk.get_status_bulk, + ) + + +class DocumentStatusBulkResourceWithStreamingResponse: + def __init__(self, document_status_bulk: DocumentStatusBulkResource) -> None: + self._document_status_bulk = document_status_bulk + + self.get_status_bulk = to_streamed_response_wrapper( + document_status_bulk.get_status_bulk, + ) + + +class AsyncDocumentStatusBulkResourceWithStreamingResponse: + def __init__(self, document_status_bulk: AsyncDocumentStatusBulkResource) -> None: + self._document_status_bulk = document_status_bulk + + self.get_status_bulk = async_to_streamed_response_wrapper( + document_status_bulk.get_status_bulk, + ) diff --git a/src/raindrop/resources/execute_query.py b/src/raindrop/resources/execute_query.py new file mode 100644 index 00000000..c6413299 --- /dev/null +++ b/src/raindrop/resources/execute_query.py @@ -0,0 +1,226 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..types import execute_query_execute_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 +from ..types.execute_query_execute_response import ExecuteQueryExecuteResponse + +__all__ = ["ExecuteQueryResource", "AsyncExecuteQueryResource"] + + +class ExecuteQueryResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExecuteQueryResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return ExecuteQueryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExecuteQueryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return ExecuteQueryResourceWithStreamingResponse(self) + + def execute( + self, + *, + smart_sql_location: execute_query_execute_params.SmartSqlLocation, + format: Optional[Literal["OUTPUT_FORMAT_UNSPECIFIED", "OUTPUT_FORMAT_JSON", "OUTPUT_FORMAT_CSV"]] | Omit = omit, + sql_query: Optional[str] | Omit = omit, + text_query: Optional[str] | 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, + ) -> ExecuteQueryExecuteResponse: + """ + Executes a SQL query or converts natural language to SQL and executes it. + Supports both direct SQL execution and AI-powered natural language to SQL + conversion. Automatically handles metadata updates and PII detection for data + governance. + + Features: + + - Direct SQL query execution + - Natural language to SQL conversion using AI + - Automatic metadata tracking for schema changes + - PII detection for security + - Multiple output formats (JSON, CSV) + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + format: Desired output format for query results + + sql_query: Direct SQL query to execute (mutually exclusive with text_query) + + text_query: Natural language query to convert to SQL (mutually exclusive with sql_query) + + 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/execute_query", + body=maybe_transform( + { + "smart_sql_location": smart_sql_location, + "format": format, + "sql_query": sql_query, + "text_query": text_query, + }, + execute_query_execute_params.ExecuteQueryExecuteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExecuteQueryExecuteResponse, + ) + + +class AsyncExecuteQueryResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExecuteQueryResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncExecuteQueryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExecuteQueryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncExecuteQueryResourceWithStreamingResponse(self) + + async def execute( + self, + *, + smart_sql_location: execute_query_execute_params.SmartSqlLocation, + format: Optional[Literal["OUTPUT_FORMAT_UNSPECIFIED", "OUTPUT_FORMAT_JSON", "OUTPUT_FORMAT_CSV"]] | Omit = omit, + sql_query: Optional[str] | Omit = omit, + text_query: Optional[str] | 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, + ) -> ExecuteQueryExecuteResponse: + """ + Executes a SQL query or converts natural language to SQL and executes it. + Supports both direct SQL execution and AI-powered natural language to SQL + conversion. Automatically handles metadata updates and PII detection for data + governance. + + Features: + + - Direct SQL query execution + - Natural language to SQL conversion using AI + - Automatic metadata tracking for schema changes + - PII detection for security + - Multiple output formats (JSON, CSV) + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + format: Desired output format for query results + + sql_query: Direct SQL query to execute (mutually exclusive with text_query) + + text_query: Natural language query to convert to SQL (mutually exclusive with sql_query) + + 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/execute_query", + body=await async_maybe_transform( + { + "smart_sql_location": smart_sql_location, + "format": format, + "sql_query": sql_query, + "text_query": text_query, + }, + execute_query_execute_params.ExecuteQueryExecuteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExecuteQueryExecuteResponse, + ) + + +class ExecuteQueryResourceWithRawResponse: + def __init__(self, execute_query: ExecuteQueryResource) -> None: + self._execute_query = execute_query + + self.execute = to_raw_response_wrapper( + execute_query.execute, + ) + + +class AsyncExecuteQueryResourceWithRawResponse: + def __init__(self, execute_query: AsyncExecuteQueryResource) -> None: + self._execute_query = execute_query + + self.execute = async_to_raw_response_wrapper( + execute_query.execute, + ) + + +class ExecuteQueryResourceWithStreamingResponse: + def __init__(self, execute_query: ExecuteQueryResource) -> None: + self._execute_query = execute_query + + self.execute = to_streamed_response_wrapper( + execute_query.execute, + ) + + +class AsyncExecuteQueryResourceWithStreamingResponse: + def __init__(self, execute_query: AsyncExecuteQueryResource) -> None: + self._execute_query = execute_query + + self.execute = async_to_streamed_response_wrapper( + execute_query.execute, + ) diff --git a/src/raindrop/resources/get_metadata.py b/src/raindrop/resources/get_metadata.py new file mode 100644 index 00000000..adfdf645 --- /dev/null +++ b/src/raindrop/resources/get_metadata.py @@ -0,0 +1,207 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import get_metadata_retrieve_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 +from ..types.get_metadata_retrieve_response import GetMetadataRetrieveResponse + +__all__ = ["GetMetadataResource", "AsyncGetMetadataResource"] + + +class GetMetadataResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GetMetadataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return GetMetadataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GetMetadataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return GetMetadataResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + smart_sql_location: get_metadata_retrieve_params.SmartSqlLocation, + table_name: Optional[str] | 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, + ) -> GetMetadataRetrieveResponse: + """Retrieves database schema metadata for a smart SQL instance. + + Returns table + structures, column information, and sample data that can be used for AI context + or application development. + + Metadata includes: + + - Table names and structures + - Column names and data types + - Sample data for AI context + - Schema versioning information + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + table_name: Optional table name to filter metadata + + 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/get_metadata", + body=maybe_transform( + { + "smart_sql_location": smart_sql_location, + "table_name": table_name, + }, + get_metadata_retrieve_params.GetMetadataRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetMetadataRetrieveResponse, + ) + + +class AsyncGetMetadataResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGetMetadataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncGetMetadataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGetMetadataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncGetMetadataResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + smart_sql_location: get_metadata_retrieve_params.SmartSqlLocation, + table_name: Optional[str] | 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, + ) -> GetMetadataRetrieveResponse: + """Retrieves database schema metadata for a smart SQL instance. + + Returns table + structures, column information, and sample data that can be used for AI context + or application development. + + Metadata includes: + + - Table names and structures + - Column names and data types + - Sample data for AI context + - Schema versioning information + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + table_name: Optional table name to filter metadata + + 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/get_metadata", + body=await async_maybe_transform( + { + "smart_sql_location": smart_sql_location, + "table_name": table_name, + }, + get_metadata_retrieve_params.GetMetadataRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetMetadataRetrieveResponse, + ) + + +class GetMetadataResourceWithRawResponse: + def __init__(self, get_metadata: GetMetadataResource) -> None: + self._get_metadata = get_metadata + + self.retrieve = to_raw_response_wrapper( + get_metadata.retrieve, + ) + + +class AsyncGetMetadataResourceWithRawResponse: + def __init__(self, get_metadata: AsyncGetMetadataResource) -> None: + self._get_metadata = get_metadata + + self.retrieve = async_to_raw_response_wrapper( + get_metadata.retrieve, + ) + + +class GetMetadataResourceWithStreamingResponse: + def __init__(self, get_metadata: GetMetadataResource) -> None: + self._get_metadata = get_metadata + + self.retrieve = to_streamed_response_wrapper( + get_metadata.retrieve, + ) + + +class AsyncGetMetadataResourceWithStreamingResponse: + def __init__(self, get_metadata: AsyncGetMetadataResource) -> None: + self._get_metadata = get_metadata + + self.retrieve = async_to_streamed_response_wrapper( + get_metadata.retrieve, + ) diff --git a/src/raindrop/resources/get_pii_data.py b/src/raindrop/resources/get_pii_data.py new file mode 100644 index 00000000..599e3fb8 --- /dev/null +++ b/src/raindrop/resources/get_pii_data.py @@ -0,0 +1,215 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import get_pii_data_retrieve_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 +from ..types.get_pii_data_retrieve_response import GetPiiDataRetrieveResponse + +__all__ = ["GetPiiDataResource", "AsyncGetPiiDataResource"] + + +class GetPiiDataResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GetPiiDataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return GetPiiDataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GetPiiDataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return GetPiiDataResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + smart_sql_location: get_pii_data_retrieve_params.SmartSqlLocation, + table_name: str, + record_id: Optional[str] | 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, + ) -> GetPiiDataRetrieveResponse: + """Retrieves PII detection results for specific database records. + + Returns detailed + information about detected personally identifiable information for compliance + and auditing purposes. + + PII information includes: + + - Entity types detected + - Confidence scores + - Character positions + - Detection timestamps + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + table_name: Table name to retrieve PII data from + + record_id: Optional record identifier to filter PII data + + 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/get_pii_data", + body=maybe_transform( + { + "smart_sql_location": smart_sql_location, + "table_name": table_name, + "record_id": record_id, + }, + get_pii_data_retrieve_params.GetPiiDataRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetPiiDataRetrieveResponse, + ) + + +class AsyncGetPiiDataResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGetPiiDataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncGetPiiDataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGetPiiDataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncGetPiiDataResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + smart_sql_location: get_pii_data_retrieve_params.SmartSqlLocation, + table_name: str, + record_id: Optional[str] | 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, + ) -> GetPiiDataRetrieveResponse: + """Retrieves PII detection results for specific database records. + + Returns detailed + information about detected personally identifiable information for compliance + and auditing purposes. + + PII information includes: + + - Entity types detected + - Confidence scores + - Character positions + - Detection timestamps + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + table_name: Table name to retrieve PII data from + + record_id: Optional record identifier to filter PII data + + 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/get_pii_data", + body=await async_maybe_transform( + { + "smart_sql_location": smart_sql_location, + "table_name": table_name, + "record_id": record_id, + }, + get_pii_data_retrieve_params.GetPiiDataRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetPiiDataRetrieveResponse, + ) + + +class GetPiiDataResourceWithRawResponse: + def __init__(self, get_pii_data: GetPiiDataResource) -> None: + self._get_pii_data = get_pii_data + + self.retrieve = to_raw_response_wrapper( + get_pii_data.retrieve, + ) + + +class AsyncGetPiiDataResourceWithRawResponse: + def __init__(self, get_pii_data: AsyncGetPiiDataResource) -> None: + self._get_pii_data = get_pii_data + + self.retrieve = async_to_raw_response_wrapper( + get_pii_data.retrieve, + ) + + +class GetPiiDataResourceWithStreamingResponse: + def __init__(self, get_pii_data: GetPiiDataResource) -> None: + self._get_pii_data = get_pii_data + + self.retrieve = to_streamed_response_wrapper( + get_pii_data.retrieve, + ) + + +class AsyncGetPiiDataResourceWithStreamingResponse: + def __init__(self, get_pii_data: AsyncGetPiiDataResource) -> None: + self._get_pii_data = get_pii_data + + self.retrieve = async_to_streamed_response_wrapper( + get_pii_data.retrieve, + ) diff --git a/src/raindrop/resources/put_memory.py b/src/raindrop/resources/put_memory.py index b2f41741..753b132c 100644 --- a/src/raindrop/resources/put_memory.py +++ b/src/raindrop/resources/put_memory.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing import Optional +from typing import Iterable, Optional import httpx -from ..types import put_memory_create_params +from ..types import put_memory_create_params, put_memory_create_batch_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 @@ -19,6 +19,7 @@ ) from .._base_client import make_request_options from ..types.put_memory_create_response import PutMemoryCreateResponse +from ..types.put_memory_create_batch_response import PutMemoryCreateBatchResponse __all__ = ["PutMemoryResource", "AsyncPutMemoryResource"] @@ -112,6 +113,63 @@ def create( cast_to=PutMemoryCreateResponse, ) + def create_batch( + self, + *, + entries: Iterable[put_memory_create_batch_params.Entry], + session_id: str, + smart_memory_location: put_memory_create_batch_params.SmartMemoryLocation, + # 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, + ) -> PutMemoryCreateBatchResponse: + """Stores multiple memory entries in a single batch operation. + + This is + significantly more efficient than calling PutMemory multiple times, as it + amortizes network overhead and batches internal storage operations. + + The system will: + + - Store all memories with automatic timestamping + - Batch generate embeddings for semantic search + - Associate memories with their specified timelines + - Return memory IDs in the same order as the input entries + + Args: + entries: Array of memory entries to store in batch + + session_id: Unique session identifier for the working memory instance + + smart_memory_location: Smart memory locator for targeting the correct smart memory instance + + 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/put_memory/batch", + body=maybe_transform( + { + "entries": entries, + "session_id": session_id, + "smart_memory_location": smart_memory_location, + }, + put_memory_create_batch_params.PutMemoryCreateBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PutMemoryCreateBatchResponse, + ) + class AsyncPutMemoryResource(AsyncAPIResource): @cached_property @@ -202,6 +260,63 @@ async def create( cast_to=PutMemoryCreateResponse, ) + async def create_batch( + self, + *, + entries: Iterable[put_memory_create_batch_params.Entry], + session_id: str, + smart_memory_location: put_memory_create_batch_params.SmartMemoryLocation, + # 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, + ) -> PutMemoryCreateBatchResponse: + """Stores multiple memory entries in a single batch operation. + + This is + significantly more efficient than calling PutMemory multiple times, as it + amortizes network overhead and batches internal storage operations. + + The system will: + + - Store all memories with automatic timestamping + - Batch generate embeddings for semantic search + - Associate memories with their specified timelines + - Return memory IDs in the same order as the input entries + + Args: + entries: Array of memory entries to store in batch + + session_id: Unique session identifier for the working memory instance + + smart_memory_location: Smart memory locator for targeting the correct smart memory instance + + 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/put_memory/batch", + body=await async_maybe_transform( + { + "entries": entries, + "session_id": session_id, + "smart_memory_location": smart_memory_location, + }, + put_memory_create_batch_params.PutMemoryCreateBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PutMemoryCreateBatchResponse, + ) + class PutMemoryResourceWithRawResponse: def __init__(self, put_memory: PutMemoryResource) -> None: @@ -210,6 +325,9 @@ def __init__(self, put_memory: PutMemoryResource) -> None: self.create = to_raw_response_wrapper( put_memory.create, ) + self.create_batch = to_raw_response_wrapper( + put_memory.create_batch, + ) class AsyncPutMemoryResourceWithRawResponse: @@ -219,6 +337,9 @@ def __init__(self, put_memory: AsyncPutMemoryResource) -> None: self.create = async_to_raw_response_wrapper( put_memory.create, ) + self.create_batch = async_to_raw_response_wrapper( + put_memory.create_batch, + ) class PutMemoryResourceWithStreamingResponse: @@ -228,6 +349,9 @@ def __init__(self, put_memory: PutMemoryResource) -> None: self.create = to_streamed_response_wrapper( put_memory.create, ) + self.create_batch = to_streamed_response_wrapper( + put_memory.create_batch, + ) class AsyncPutMemoryResourceWithStreamingResponse: @@ -237,3 +361,6 @@ def __init__(self, put_memory: AsyncPutMemoryResource) -> None: self.create = async_to_streamed_response_wrapper( put_memory.create, ) + self.create_batch = async_to_streamed_response_wrapper( + put_memory.create_batch, + ) diff --git a/src/raindrop/resources/rehydration_status.py b/src/raindrop/resources/rehydration_status.py new file mode 100644 index 00000000..5857fec3 --- /dev/null +++ b/src/raindrop/resources/rehydration_status.py @@ -0,0 +1,191 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import rehydration_status_create_params +from .._types import Body, Query, Headers, NotGiven, 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.rehydration_status_create_response import RehydrationStatusCreateResponse + +__all__ = ["RehydrationStatusResource", "AsyncRehydrationStatusResource"] + + +class RehydrationStatusResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RehydrationStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return RehydrationStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RehydrationStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return RehydrationStatusResourceWithStreamingResponse(self) + + def create( + self, + *, + session_id: str, + smart_memory_location: rehydration_status_create_params.SmartMemoryLocation, + # 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, + ) -> RehydrationStatusCreateResponse: + """Gets the current status of an async rehydration operation. + + Use this to poll for + completion after calling RehydrateSession. Returns status information including + progress and any errors. + + Args: + session_id: Session identifier to check rehydration status for + + smart_memory_location: Smart memory locator for targeting the correct smart memory instance + + 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/rehydration_status", + body=maybe_transform( + { + "session_id": session_id, + "smart_memory_location": smart_memory_location, + }, + rehydration_status_create_params.RehydrationStatusCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RehydrationStatusCreateResponse, + ) + + +class AsyncRehydrationStatusResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRehydrationStatusResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncRehydrationStatusResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRehydrationStatusResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncRehydrationStatusResourceWithStreamingResponse(self) + + async def create( + self, + *, + session_id: str, + smart_memory_location: rehydration_status_create_params.SmartMemoryLocation, + # 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, + ) -> RehydrationStatusCreateResponse: + """Gets the current status of an async rehydration operation. + + Use this to poll for + completion after calling RehydrateSession. Returns status information including + progress and any errors. + + Args: + session_id: Session identifier to check rehydration status for + + smart_memory_location: Smart memory locator for targeting the correct smart memory instance + + 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/rehydration_status", + body=await async_maybe_transform( + { + "session_id": session_id, + "smart_memory_location": smart_memory_location, + }, + rehydration_status_create_params.RehydrationStatusCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RehydrationStatusCreateResponse, + ) + + +class RehydrationStatusResourceWithRawResponse: + def __init__(self, rehydration_status: RehydrationStatusResource) -> None: + self._rehydration_status = rehydration_status + + self.create = to_raw_response_wrapper( + rehydration_status.create, + ) + + +class AsyncRehydrationStatusResourceWithRawResponse: + def __init__(self, rehydration_status: AsyncRehydrationStatusResource) -> None: + self._rehydration_status = rehydration_status + + self.create = async_to_raw_response_wrapper( + rehydration_status.create, + ) + + +class RehydrationStatusResourceWithStreamingResponse: + def __init__(self, rehydration_status: RehydrationStatusResource) -> None: + self._rehydration_status = rehydration_status + + self.create = to_streamed_response_wrapper( + rehydration_status.create, + ) + + +class AsyncRehydrationStatusResourceWithStreamingResponse: + def __init__(self, rehydration_status: AsyncRehydrationStatusResource) -> None: + self._rehydration_status = rehydration_status + + self.create = async_to_streamed_response_wrapper( + rehydration_status.create, + ) diff --git a/src/raindrop/resources/update_metadata.py b/src/raindrop/resources/update_metadata.py new file mode 100644 index 00000000..11cfef79 --- /dev/null +++ b/src/raindrop/resources/update_metadata.py @@ -0,0 +1,220 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ..types import update_metadata_update_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 +from ..types.update_metadata_update_response import UpdateMetadataUpdateResponse + +__all__ = ["UpdateMetadataResource", "AsyncUpdateMetadataResource"] + + +class UpdateMetadataResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UpdateMetadataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return UpdateMetadataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UpdateMetadataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return UpdateMetadataResourceWithStreamingResponse(self) + + def update( + self, + *, + smart_sql_location: update_metadata_update_params.SmartSqlLocation, + tables: Iterable[update_metadata_update_params.Table], + mode: Optional[ + Literal["UPDATE_MODE_UNSPECIFIED", "UPDATE_MODE_REPLACE", "UPDATE_MODE_MERGE", "UPDATE_MODE_APPEND"] + ] + | 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, + ) -> UpdateMetadataUpdateResponse: + """Updates database schema metadata manually. + + Allows for explicit metadata + management when automatic detection is insufficient or needs correction. + + Use cases: + + - Manual schema corrections + - Bulk metadata updates + - Custom metadata annotations + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + tables: Table metadata to update or create + + mode: Update mode: replace (overwrite), merge (preserve existing), or append (only new + entries) + + 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/update_metadata", + body=maybe_transform( + { + "smart_sql_location": smart_sql_location, + "tables": tables, + "mode": mode, + }, + update_metadata_update_params.UpdateMetadataUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UpdateMetadataUpdateResponse, + ) + + +class AsyncUpdateMetadataResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUpdateMetadataResourceWithRawResponse: + """ + 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/LiquidMetal-AI/lm-raindrop-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncUpdateMetadataResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUpdateMetadataResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/LiquidMetal-AI/lm-raindrop-python-sdk#with_streaming_response + """ + return AsyncUpdateMetadataResourceWithStreamingResponse(self) + + async def update( + self, + *, + smart_sql_location: update_metadata_update_params.SmartSqlLocation, + tables: Iterable[update_metadata_update_params.Table], + mode: Optional[ + Literal["UPDATE_MODE_UNSPECIFIED", "UPDATE_MODE_REPLACE", "UPDATE_MODE_MERGE", "UPDATE_MODE_APPEND"] + ] + | 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, + ) -> UpdateMetadataUpdateResponse: + """Updates database schema metadata manually. + + Allows for explicit metadata + management when automatic detection is insufficient or needs correction. + + Use cases: + + - Manual schema corrections + - Bulk metadata updates + - Custom metadata annotations + + Args: + smart_sql_location: Smart SQL locator for targeting the correct smart SQL instance + + tables: Table metadata to update or create + + mode: Update mode: replace (overwrite), merge (preserve existing), or append (only new + entries) + + 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/update_metadata", + body=await async_maybe_transform( + { + "smart_sql_location": smart_sql_location, + "tables": tables, + "mode": mode, + }, + update_metadata_update_params.UpdateMetadataUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UpdateMetadataUpdateResponse, + ) + + +class UpdateMetadataResourceWithRawResponse: + def __init__(self, update_metadata: UpdateMetadataResource) -> None: + self._update_metadata = update_metadata + + self.update = to_raw_response_wrapper( + update_metadata.update, + ) + + +class AsyncUpdateMetadataResourceWithRawResponse: + def __init__(self, update_metadata: AsyncUpdateMetadataResource) -> None: + self._update_metadata = update_metadata + + self.update = async_to_raw_response_wrapper( + update_metadata.update, + ) + + +class UpdateMetadataResourceWithStreamingResponse: + def __init__(self, update_metadata: UpdateMetadataResource) -> None: + self._update_metadata = update_metadata + + self.update = to_streamed_response_wrapper( + update_metadata.update, + ) + + +class AsyncUpdateMetadataResourceWithStreamingResponse: + def __init__(self, update_metadata: AsyncUpdateMetadataResource) -> None: + self._update_metadata = update_metadata + + self.update = async_to_streamed_response_wrapper( + update_metadata.update, + ) diff --git a/src/raindrop/types/__init__.py b/src/raindrop/types/__init__.py index 189cd9ef..07a209c4 100644 --- a/src/raindrop/types/__init__.py +++ b/src/raindrop/types/__init__.py @@ -29,7 +29,10 @@ from .query_chunk_search_response import QueryChunkSearchResponse as QueryChunkSearchResponse from .query_document_query_params import QueryDocumentQueryParams as QueryDocumentQueryParams from .start_session_create_params import StartSessionCreateParams as StartSessionCreateParams +from .execute_query_execute_params import ExecuteQueryExecuteParams as ExecuteQueryExecuteParams from .get_memory_retrieve_response import GetMemoryRetrieveResponse as GetMemoryRetrieveResponse +from .get_metadata_retrieve_params import GetMetadataRetrieveParams as GetMetadataRetrieveParams +from .get_pii_data_retrieve_params import GetPiiDataRetrieveParams as GetPiiDataRetrieveParams from .list_procedure_create_params import ListProcedureCreateParams as ListProcedureCreateParams from .query_sumarize_page_response import QuerySumarizePageResponse as QuerySumarizePageResponse from .delete_memory_create_response import DeleteMemoryCreateResponse as DeleteMemoryCreateResponse @@ -37,17 +40,28 @@ from .put_procedure_create_response import PutProcedureCreateResponse as PutProcedureCreateResponse from .query_document_query_response import QueryDocumentQueryResponse as QueryDocumentQueryResponse from .start_session_create_response import StartSessionCreateResponse as StartSessionCreateResponse +from .update_metadata_update_params import UpdateMetadataUpdateParams as UpdateMetadataUpdateParams from .delete_procedure_create_params import DeleteProcedureCreateParams as DeleteProcedureCreateParams +from .execute_query_execute_response import ExecuteQueryExecuteResponse as ExecuteQueryExecuteResponse +from .get_metadata_retrieve_response import GetMetadataRetrieveResponse as GetMetadataRetrieveResponse +from .get_pii_data_retrieve_response import GetPiiDataRetrieveResponse as GetPiiDataRetrieveResponse from .list_procedure_create_response import ListProcedureCreateResponse as ListProcedureCreateResponse +from .put_memory_create_batch_params import PutMemoryCreateBatchParams as PutMemoryCreateBatchParams from .summarize_memory_create_params import SummarizeMemoryCreateParams as SummarizeMemoryCreateParams +from .update_metadata_update_response import UpdateMetadataUpdateResponse as UpdateMetadataUpdateResponse from .delete_procedure_create_response import DeleteProcedureCreateResponse as DeleteProcedureCreateResponse from .liquidmetal_v1alpha1_text_result import LiquidmetalV1alpha1TextResult as LiquidmetalV1alpha1TextResult +from .put_memory_create_batch_response import PutMemoryCreateBatchResponse as PutMemoryCreateBatchResponse +from .rehydration_status_create_params import RehydrationStatusCreateParams as RehydrationStatusCreateParams from .summarize_memory_create_response import SummarizeMemoryCreateResponse as SummarizeMemoryCreateResponse +from .document_status_get_status_params import DocumentStatusGetStatusParams as DocumentStatusGetStatusParams from .get_semantic_memory_create_params import GetSemanticMemoryCreateParams as GetSemanticMemoryCreateParams from .put_semantic_memory_create_params import PutSemanticMemoryCreateParams as PutSemanticMemoryCreateParams from .query_get_paginated_search_params import QueryGetPaginatedSearchParams as QueryGetPaginatedSearchParams from .liquidmetal_v1alpha1_source_result import LiquidmetalV1alpha1SourceResult as LiquidmetalV1alpha1SourceResult from .rehydrate_session_rehydrate_params import RehydrateSessionRehydrateParams as RehydrateSessionRehydrateParams +from .rehydration_status_create_response import RehydrationStatusCreateResponse as RehydrationStatusCreateResponse +from .document_status_get_status_response import DocumentStatusGetStatusResponse as DocumentStatusGetStatusResponse from .get_semantic_memory_create_response import GetSemanticMemoryCreateResponse as GetSemanticMemoryCreateResponse from .put_semantic_memory_create_response import PutSemanticMemoryCreateResponse as PutSemanticMemoryCreateResponse from .delete_semantic_memory_delete_params import DeleteSemanticMemoryDeleteParams as DeleteSemanticMemoryDeleteParams @@ -58,3 +72,9 @@ from .liquidmetal_v1alpha1_bucket_name_param import ( LiquidmetalV1alpha1BucketNameParam as LiquidmetalV1alpha1BucketNameParam, ) +from .document_status_bulk_get_status_bulk_params import ( + DocumentStatusBulkGetStatusBulkParams as DocumentStatusBulkGetStatusBulkParams, +) +from .document_status_bulk_get_status_bulk_response import ( + DocumentStatusBulkGetStatusBulkResponse as DocumentStatusBulkGetStatusBulkResponse, +) diff --git a/src/raindrop/types/bucket/__init__.py b/src/raindrop/types/bucket/__init__.py new file mode 100644 index 00000000..2e6f63c4 --- /dev/null +++ b/src/raindrop/types/bucket/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .by_status_list_objects_params import ByStatusListObjectsParams as ByStatusListObjectsParams +from .by_status_list_objects_response import ByStatusListObjectsResponse as ByStatusListObjectsResponse diff --git a/src/raindrop/types/bucket/by_status_list_objects_params.py b/src/raindrop/types/bucket/by_status_list_objects_params.py new file mode 100644 index 00000000..b32b8a94 --- /dev/null +++ b/src/raindrop/types/bucket/by_status_list_objects_params.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, Annotated, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo +from ..bucket_locator_param import BucketLocatorParam + +__all__ = ["ByStatusListObjectsParams"] + + +class ByStatusListObjectsParams(TypedDict, total=False): + bucket_location: Required[Annotated[BucketLocatorParam, PropertyInfo(alias="bucketLocation")]] + """The storage bucket to query""" + + statuses: Required[SequenceNotStr[str]] + """ + Status values to filter by (e.g., "completed", "failed", "processing", + "ingesting", "partial", "uploading", "not_found") + """ + + exclude: Optional[bool] + """ + If true, returns objects NOT matching the specified statuses (inverts the + filter) + """ + + partition: Optional[str] + """Partition to query (defaults to "default")""" + + prefix: Optional[str] + """ + Optional prefix to filter object keys (e.g., "documents/" to only search in + documents folder) + """ diff --git a/src/raindrop/types/bucket/by_status_list_objects_response.py b/src/raindrop/types/bucket/by_status_list_objects_response.py new file mode 100644 index 00000000..47d14ab9 --- /dev/null +++ b/src/raindrop/types/bucket/by_status_list_objects_response.py @@ -0,0 +1,101 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "ByStatusListObjectsResponse", + "Document", + "DocumentEmbedding", + "DocumentIngest", + "DocumentPii", + "DocumentRelationships", + "DocumentVectorIndex", +] + + +class DocumentEmbedding(BaseModel): + """Embedding stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentIngest(BaseModel): + """Ingest stage information""" + + chunks_queued: Optional[int] = FieldInfo(alias="chunksQueued", default=None) + """Number of chunks queued for processing""" + + creation_complete: Optional[bool] = FieldInfo(alias="creationComplete", default=None) + """Whether chunk creation is complete""" + + total_chunks_created: Optional[int] = FieldInfo(alias="totalChunksCreated", default=None) + """Total number of chunks created""" + + +class DocumentPii(BaseModel): + """PII detection stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentRelationships(BaseModel): + """Relationships stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentVectorIndex(BaseModel): + """Vector index stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class Document(BaseModel): + embedding: Optional[DocumentEmbedding] = None + """Embedding stage information""" + + errors: Optional[List[str]] = None + """Any errors encountered during indexing""" + + ingest: Optional[DocumentIngest] = None + """Ingest stage information""" + + object_id: Optional[str] = FieldInfo(alias="objectId", default=None) + """Document identifier (object key)""" + + pii: Optional[DocumentPii] = None + """PII detection stage information""" + + relationships: Optional[DocumentRelationships] = None + """Relationships stage information""" + + status: Optional[str] = None + """Overall document status""" + + vector_index: Optional[DocumentVectorIndex] = FieldInfo(alias="vectorIndex", default=None) + """Vector index stage information""" + + +class ByStatusListObjectsResponse(BaseModel): + documents: Optional[List[Document]] = None + """Documents matching the status filter with their full status information""" diff --git a/src/raindrop/types/bucket_list_params.py b/src/raindrop/types/bucket_list_params.py index c132d17a..1b315c70 100644 --- a/src/raindrop/types/bucket_list_params.py +++ b/src/raindrop/types/bucket_list_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -16,3 +17,9 @@ class BucketListParams(TypedDict, total=False): If provided, the search will only return results from these buckets """ + + prefix: Optional[str] + """ + Optional prefix to filter object keys (e.g., "documents/" to only list objects + in documents folder) + """ diff --git a/src/raindrop/types/bucket_list_response.py b/src/raindrop/types/bucket_list_response.py index 2d9be87e..d180bdc3 100644 --- a/src/raindrop/types/bucket_list_response.py +++ b/src/raindrop/types/bucket_list_response.py @@ -11,6 +11,8 @@ class Object(BaseModel): + """ObjectInfo represents metadata about a single object""" + content_type: str = FieldInfo(alias="contentType") """MIME type of the object""" diff --git a/src/raindrop/types/bucket_locator_param.py b/src/raindrop/types/bucket_locator_param.py index 593afede..b1aebc62 100644 --- a/src/raindrop/types/bucket_locator_param.py +++ b/src/raindrop/types/bucket_locator_param.py @@ -12,7 +12,10 @@ class Bucket(TypedDict, total=False): bucket: Required[LiquidmetalV1alpha1BucketNameParam] - """**EXAMPLE** { name: 'my-smartbucket' } **REQUIRED** FALSE""" + """ + **EXAMPLE** { name: 'my-smartbucket', version: '01jtryx2f2f61ryk06vd8mr91p', + application_name: 'my-app' } **REQUIRED** FALSE + """ BucketLocatorParam: TypeAlias = Union[Bucket, object] diff --git a/src/raindrop/types/document_status_bulk_get_status_bulk_params.py b/src/raindrop/types/document_status_bulk_get_status_bulk_params.py new file mode 100644 index 00000000..4107c148 --- /dev/null +++ b/src/raindrop/types/document_status_bulk_get_status_bulk_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 import Optional +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .bucket_locator_param import BucketLocatorParam + +__all__ = ["DocumentStatusBulkGetStatusBulkParams"] + + +class DocumentStatusBulkGetStatusBulkParams(TypedDict, total=False): + bucket_location: Required[Annotated[BucketLocatorParam, PropertyInfo(alias="bucketLocation")]] + """The storage bucket containing the target documents""" + + object_ids: Required[Annotated[SequenceNotStr[str], PropertyInfo(alias="objectIds")]] + """List of document identifiers (object keys) to get status for""" + + partition: Optional[str] + """Optional partition identifier for multi-tenant data isolation. + + Defaults to 'default' if not specified + """ diff --git a/src/raindrop/types/document_status_bulk_get_status_bulk_response.py b/src/raindrop/types/document_status_bulk_get_status_bulk_response.py new file mode 100644 index 00000000..fcbef713 --- /dev/null +++ b/src/raindrop/types/document_status_bulk_get_status_bulk_response.py @@ -0,0 +1,101 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = [ + "DocumentStatusBulkGetStatusBulkResponse", + "Document", + "DocumentEmbedding", + "DocumentIngest", + "DocumentPii", + "DocumentRelationships", + "DocumentVectorIndex", +] + + +class DocumentEmbedding(BaseModel): + """Embedding stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentIngest(BaseModel): + """Ingest stage information""" + + chunks_queued: Optional[int] = FieldInfo(alias="chunksQueued", default=None) + """Number of chunks queued for processing""" + + creation_complete: Optional[bool] = FieldInfo(alias="creationComplete", default=None) + """Whether chunk creation is complete""" + + total_chunks_created: Optional[int] = FieldInfo(alias="totalChunksCreated", default=None) + """Total number of chunks created""" + + +class DocumentPii(BaseModel): + """PII detection stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentRelationships(BaseModel): + """Relationships stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentVectorIndex(BaseModel): + """Vector index stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class Document(BaseModel): + embedding: Optional[DocumentEmbedding] = None + """Embedding stage information""" + + errors: Optional[List[str]] = None + """Any errors encountered during indexing""" + + ingest: Optional[DocumentIngest] = None + """Ingest stage information""" + + object_id: Optional[str] = FieldInfo(alias="objectId", default=None) + """Document identifier (object key)""" + + pii: Optional[DocumentPii] = None + """PII detection stage information""" + + relationships: Optional[DocumentRelationships] = None + """Relationships stage information""" + + status: Optional[str] = None + """Overall document status""" + + vector_index: Optional[DocumentVectorIndex] = FieldInfo(alias="vectorIndex", default=None) + """Vector index stage information""" + + +class DocumentStatusBulkGetStatusBulkResponse(BaseModel): + documents: Optional[List[Document]] = None + """Status information for each requested document, keyed by object_id""" diff --git a/src/raindrop/types/document_status_get_status_params.py b/src/raindrop/types/document_status_get_status_params.py new file mode 100644 index 00000000..9dae0902 --- /dev/null +++ b/src/raindrop/types/document_status_get_status_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo +from .bucket_locator_param import BucketLocatorParam + +__all__ = ["DocumentStatusGetStatusParams"] + + +class DocumentStatusGetStatusParams(TypedDict, total=False): + bucket_location: Required[Annotated[BucketLocatorParam, PropertyInfo(alias="bucketLocation")]] + """The storage bucket containing the target document""" + + object_id: Required[Annotated[str, PropertyInfo(alias="objectId")]] + """Document identifier within the bucket (object key)""" + + partition: Optional[str] + """Optional partition identifier for multi-tenant data isolation. + + Defaults to 'default' if not specified + """ diff --git a/src/raindrop/types/document_status_get_status_response.py b/src/raindrop/types/document_status_get_status_response.py new file mode 100644 index 00000000..ae65a5c7 --- /dev/null +++ b/src/raindrop/types/document_status_get_status_response.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentStatusGetStatusResponse", "Embedding", "Ingest", "Pii", "Relationships", "VectorIndex"] + + +class Embedding(BaseModel): + """Embedding stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class Ingest(BaseModel): + """Ingest stage information""" + + chunks_queued: Optional[int] = FieldInfo(alias="chunksQueued", default=None) + """Number of chunks queued for processing""" + + creation_complete: Optional[bool] = FieldInfo(alias="creationComplete", default=None) + """Whether chunk creation is complete""" + + total_chunks_created: Optional[int] = FieldInfo(alias="totalChunksCreated", default=None) + """Total number of chunks created""" + + +class Pii(BaseModel): + """PII detection stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class Relationships(BaseModel): + """Relationships stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class VectorIndex(BaseModel): + """Vector index stage information""" + + items_remaining: Optional[int] = FieldInfo(alias="itemsRemaining", default=None) + """Number of items remaining to be processed""" + + total_expected: Optional[int] = FieldInfo(alias="totalExpected", default=None) + """Total number of items expected to be processed""" + + +class DocumentStatusGetStatusResponse(BaseModel): + embedding: Optional[Embedding] = None + """Embedding stage information""" + + errors: Optional[List[str]] = None + """Any errors encountered during indexing""" + + ingest: Optional[Ingest] = None + """Ingest stage information""" + + pii: Optional[Pii] = None + """PII detection stage information""" + + relationships: Optional[Relationships] = None + """Relationships stage information""" + + status: Optional[str] = None + """Overall document status""" + + vector_index: Optional[VectorIndex] = FieldInfo(alias="vectorIndex", default=None) + """Vector index stage information""" diff --git a/src/raindrop/types/execute_query_execute_params.py b/src/raindrop/types/execute_query_execute_params.py new file mode 100644 index 00000000..a84b38d1 --- /dev/null +++ b/src/raindrop/types/execute_query_execute_params.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "ExecuteQueryExecuteParams", + "SmartSqlLocation", + "SmartSqlLocationSmartSql", + "SmartSqlLocationSmartSqlSmartSql", +] + + +class ExecuteQueryExecuteParams(TypedDict, total=False): + smart_sql_location: Required[Annotated[SmartSqlLocation, PropertyInfo(alias="smartSqlLocation")]] + """Smart SQL locator for targeting the correct smart SQL instance""" + + format: Optional[Literal["OUTPUT_FORMAT_UNSPECIFIED", "OUTPUT_FORMAT_JSON", "OUTPUT_FORMAT_CSV"]] + """Desired output format for query results""" + + sql_query: Annotated[Optional[str], PropertyInfo(alias="sqlQuery")] + """Direct SQL query to execute (mutually exclusive with text_query)""" + + text_query: Annotated[Optional[str], PropertyInfo(alias="textQuery")] + """Natural language query to convert to SQL (mutually exclusive with sql_query)""" + + +class SmartSqlLocationSmartSqlSmartSql(TypedDict, total=False): + """Name-based smart SQL instance identifier (recommended)""" + + name: Required[str] + """The name of the smart SQL instance""" + + application_name: Annotated[Optional[str], PropertyInfo(alias="applicationName")] + """Optional application name that owns this smart SQL instance""" + + version: Optional[str] + """Optional version identifier for the smart SQL instance""" + + +class SmartSqlLocationSmartSql(TypedDict, total=False): + smart_sql: Required[Annotated[SmartSqlLocationSmartSqlSmartSql, PropertyInfo(alias="smartSql")]] + """Name-based smart SQL instance identifier (recommended)""" + + +SmartSqlLocation: TypeAlias = Union[SmartSqlLocationSmartSql, object] diff --git a/src/raindrop/types/execute_query_execute_response.py b/src/raindrop/types/execute_query_execute_response.py new file mode 100644 index 00000000..eda3061e --- /dev/null +++ b/src/raindrop/types/execute_query_execute_response.py @@ -0,0 +1,26 @@ +# 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 + +__all__ = ["ExecuteQueryExecuteResponse"] + + +class ExecuteQueryExecuteResponse(BaseModel): + ai_reasoning: Optional[str] = FieldInfo(alias="aiReasoning", default=None) + """AI reasoning for natural language queries (if applicable)""" + + format: Optional[str] = None + """Format of the results field""" + + query_executed: Optional[str] = FieldInfo(alias="queryExecuted", default=None) + """The actual SQL query that was executed""" + + results: Optional[str] = None + """Query results as a string in the requested format (JSON or CSV)""" + + status: Optional[int] = None + """HTTP status code for the operation""" diff --git a/src/raindrop/types/get_metadata_retrieve_params.py b/src/raindrop/types/get_metadata_retrieve_params.py new file mode 100644 index 00000000..fb1b1177 --- /dev/null +++ b/src/raindrop/types/get_metadata_retrieve_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "GetMetadataRetrieveParams", + "SmartSqlLocation", + "SmartSqlLocationSmartSql", + "SmartSqlLocationSmartSqlSmartSql", +] + + +class GetMetadataRetrieveParams(TypedDict, total=False): + smart_sql_location: Required[Annotated[SmartSqlLocation, PropertyInfo(alias="smartSqlLocation")]] + """Smart SQL locator for targeting the correct smart SQL instance""" + + table_name: Annotated[Optional[str], PropertyInfo(alias="tableName")] + """Optional table name to filter metadata""" + + +class SmartSqlLocationSmartSqlSmartSql(TypedDict, total=False): + """Name-based smart SQL instance identifier (recommended)""" + + name: Required[str] + """The name of the smart SQL instance""" + + application_name: Annotated[Optional[str], PropertyInfo(alias="applicationName")] + """Optional application name that owns this smart SQL instance""" + + version: Optional[str] + """Optional version identifier for the smart SQL instance""" + + +class SmartSqlLocationSmartSql(TypedDict, total=False): + smart_sql: Required[Annotated[SmartSqlLocationSmartSqlSmartSql, PropertyInfo(alias="smartSql")]] + """Name-based smart SQL instance identifier (recommended)""" + + +SmartSqlLocation: TypeAlias = Union[SmartSqlLocationSmartSql, object] diff --git a/src/raindrop/types/get_metadata_retrieve_response.py b/src/raindrop/types/get_metadata_retrieve_response.py new file mode 100644 index 00000000..04cd585e --- /dev/null +++ b/src/raindrop/types/get_metadata_retrieve_response.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["GetMetadataRetrieveResponse", "Table", "TableColumn"] + + +class TableColumn(BaseModel): + column_name: Optional[str] = FieldInfo(alias="columnName", default=None) + """Name of the database column""" + + data_type: Optional[str] = FieldInfo(alias="dataType", default=None) + """Data type of the column""" + + is_primary_key: Optional[bool] = FieldInfo(alias="isPrimaryKey", default=None) + """Whether the column is a primary key""" + + nullable: Optional[bool] = None + """Whether the column can contain null values""" + + sample_data: Optional[str] = FieldInfo(alias="sampleData", default=None) + """Sample data for AI context (nullable)""" + + +class Table(BaseModel): + columns: Optional[List[TableColumn]] = None + """List of column information for the table""" + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + """When this table metadata was created""" + + table_name: Optional[str] = FieldInfo(alias="tableName", default=None) + """Name of the database table""" + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + """When this table metadata was last updated""" + + +class GetMetadataRetrieveResponse(BaseModel): + last_updated: Optional[datetime] = FieldInfo(alias="lastUpdated", default=None) + """Timestamp when metadata was last updated""" + + tables: Optional[List[Table]] = None + """List of table metadata entries""" diff --git a/src/raindrop/types/get_pii_data_retrieve_params.py b/src/raindrop/types/get_pii_data_retrieve_params.py new file mode 100644 index 00000000..021e5569 --- /dev/null +++ b/src/raindrop/types/get_pii_data_retrieve_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 import Union, Optional +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "GetPiiDataRetrieveParams", + "SmartSqlLocation", + "SmartSqlLocationSmartSql", + "SmartSqlLocationSmartSqlSmartSql", +] + + +class GetPiiDataRetrieveParams(TypedDict, total=False): + smart_sql_location: Required[Annotated[SmartSqlLocation, PropertyInfo(alias="smartSqlLocation")]] + """Smart SQL locator for targeting the correct smart SQL instance""" + + table_name: Required[Annotated[str, PropertyInfo(alias="tableName")]] + """Table name to retrieve PII data from""" + + record_id: Annotated[Optional[str], PropertyInfo(alias="recordId")] + """Optional record identifier to filter PII data""" + + +class SmartSqlLocationSmartSqlSmartSql(TypedDict, total=False): + """Name-based smart SQL instance identifier (recommended)""" + + name: Required[str] + """The name of the smart SQL instance""" + + application_name: Annotated[Optional[str], PropertyInfo(alias="applicationName")] + """Optional application name that owns this smart SQL instance""" + + version: Optional[str] + """Optional version identifier for the smart SQL instance""" + + +class SmartSqlLocationSmartSql(TypedDict, total=False): + smart_sql: Required[Annotated[SmartSqlLocationSmartSqlSmartSql, PropertyInfo(alias="smartSql")]] + """Name-based smart SQL instance identifier (recommended)""" + + +SmartSqlLocation: TypeAlias = Union[SmartSqlLocationSmartSql, object] diff --git a/src/raindrop/types/get_pii_data_retrieve_response.py b/src/raindrop/types/get_pii_data_retrieve_response.py new file mode 100644 index 00000000..40232305 --- /dev/null +++ b/src/raindrop/types/get_pii_data_retrieve_response.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["GetPiiDataRetrieveResponse", "PiiDetection", "PiiDetectionEntity"] + + +class PiiDetectionEntity(BaseModel): + confidence_score: Optional[float] = FieldInfo(alias="confidenceScore", default=None) + """Confidence score for this detection (0.0 to 1.0)""" + + detected_text: Optional[str] = FieldInfo(alias="detectedText", default=None) + """The detected text/token""" + + end_position: Optional[int] = FieldInfo(alias="endPosition", default=None) + """End character position in the original text""" + + entity_type: Optional[str] = FieldInfo(alias="entityType", default=None) + """Type of PII entity detected""" + + start_position: Optional[int] = FieldInfo(alias="startPosition", default=None) + """Start character position in the original text""" + + token_index: Optional[int] = FieldInfo(alias="tokenIndex", default=None) + """Token index in the tokenized text""" + + +class PiiDetection(BaseModel): + detected_at: Optional[datetime] = FieldInfo(alias="detectedAt", default=None) + """When the PII detection was performed""" + + detection_id: Optional[str] = FieldInfo(alias="detectionId", default=None) + """Unique identifier for this PII detection record""" + + entities: Optional[List[PiiDetectionEntity]] = None + """List of detected PII entities""" + + record_id: Optional[str] = FieldInfo(alias="recordId", default=None) + """Record identifier within the table""" + + table_name: Optional[str] = FieldInfo(alias="tableName", default=None) + """Table name where PII was detected""" + + +class GetPiiDataRetrieveResponse(BaseModel): + pii_detections: Optional[List[PiiDetection]] = FieldInfo(alias="piiDetections", default=None) + """List of PII detection results""" diff --git a/src/raindrop/types/liquidmetal_v1alpha1_bucket_name_param.py b/src/raindrop/types/liquidmetal_v1alpha1_bucket_name_param.py index b658fe31..3eea8421 100644 --- a/src/raindrop/types/liquidmetal_v1alpha1_bucket_name_param.py +++ b/src/raindrop/types/liquidmetal_v1alpha1_bucket_name_param.py @@ -2,7 +2,6 @@ from __future__ import annotations -from typing import Optional from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -11,14 +10,16 @@ class LiquidmetalV1alpha1BucketNameParam(TypedDict, total=False): + """BucketName represents a bucket name with version and application name""" + + application_name: Required[Annotated[str, PropertyInfo(alias="applicationName")]] + """The application name **EXAMPLE** "my-app" **REQUIRED** TRUE""" + name: Required[str] """The name of the bucket **EXAMPLE** "my-bucket" **REQUIRED** TRUE""" - application_name: Annotated[Optional[str], PropertyInfo(alias="applicationName")] - """Optional Application **EXAMPLE** "my-app" **REQUIRED** FALSE""" - - version: Optional[str] + version: Required[str] """ - Optional version of the bucket **EXAMPLE** "01jtryx2f2f61ryk06vd8mr91p" - **REQUIRED** FALSE + The version of the bucket **EXAMPLE** "01jtryx2f2f61ryk06vd8mr91p" **REQUIRED** + TRUE """ diff --git a/src/raindrop/types/put_memory_create_batch_params.py b/src/raindrop/types/put_memory_create_batch_params.py new file mode 100644 index 00000000..42e52130 --- /dev/null +++ b/src/raindrop/types/put_memory_create_batch_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 import Union, Iterable, Optional +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo +from .shared_params.liquidmetal_v1alpha1_smart_memory_name import LiquidmetalV1alpha1SmartMemoryName + +__all__ = ["PutMemoryCreateBatchParams", "Entry", "SmartMemoryLocation", "SmartMemoryLocationSmartMemory"] + + +class PutMemoryCreateBatchParams(TypedDict, total=False): + entries: Required[Iterable[Entry]] + """Array of memory entries to store in batch""" + + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] + """Unique session identifier for the working memory instance""" + + smart_memory_location: Required[Annotated[SmartMemoryLocation, PropertyInfo(alias="smartMemoryLocation")]] + """Smart memory locator for targeting the correct smart memory instance""" + + +class Entry(TypedDict, total=False): + content: Required[str] + """The actual memory content to store""" + + agent: Optional[str] + """Agent identifier responsible for this memory""" + + key: Optional[str] + """Optional key for direct memory retrieval""" + + timeline: Optional[str] + """Timeline identifier for organizing related memories""" + + +class SmartMemoryLocationSmartMemory(TypedDict, total=False): + smart_memory: Required[Annotated[LiquidmetalV1alpha1SmartMemoryName, PropertyInfo(alias="smartMemory")]] + """ + **EXAMPLE** {"name":"memory-name","application_name":"demo","version":"1234"} + **REQUIRED** FALSE + """ + + +SmartMemoryLocation: TypeAlias = Union[SmartMemoryLocationSmartMemory, object] diff --git a/src/raindrop/types/put_memory_create_batch_response.py b/src/raindrop/types/put_memory_create_batch_response.py new file mode 100644 index 00000000..e568ecf2 --- /dev/null +++ b/src/raindrop/types/put_memory_create_batch_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["PutMemoryCreateBatchResponse"] + + +class PutMemoryCreateBatchResponse(BaseModel): + memory_ids: Optional[List[str]] = FieldInfo(alias="memoryIds", default=None) + """Unique identifiers for each stored memory entry, in same order as input""" diff --git a/src/raindrop/types/query/episodic_memory_search_response.py b/src/raindrop/types/query/episodic_memory_search_response.py index ec3efb74..f2354691 100644 --- a/src/raindrop/types/query/episodic_memory_search_response.py +++ b/src/raindrop/types/query/episodic_memory_search_response.py @@ -37,6 +37,8 @@ class Entry(BaseModel): class Pagination(BaseModel): + """Pagination information for the search results""" + has_more: Optional[bool] = FieldInfo(alias="hasMore", default=None) """Whether there are more results available""" diff --git a/src/raindrop/types/query/semantic_memory_search_response.py b/src/raindrop/types/query/semantic_memory_search_response.py index 07a38317..57d0ded0 100644 --- a/src/raindrop/types/query/semantic_memory_search_response.py +++ b/src/raindrop/types/query/semantic_memory_search_response.py @@ -33,6 +33,8 @@ class DocumentSearchResponseResult(BaseModel): class DocumentSearchResponse(BaseModel): + """Search results with matching documents""" + results: Optional[List[DocumentSearchResponseResult]] = None """List of matching documents ordered by relevance""" diff --git a/src/raindrop/types/query_search_response.py b/src/raindrop/types/query_search_response.py index 7357d9c0..63be71ef 100644 --- a/src/raindrop/types/query_search_response.py +++ b/src/raindrop/types/query_search_response.py @@ -11,6 +11,8 @@ class Pagination(BaseModel): + """Pagination details for result navigation""" + page: int """Current page number (1-based)""" diff --git a/src/raindrop/types/rehydration_status_create_params.py b/src/raindrop/types/rehydration_status_create_params.py new file mode 100644 index 00000000..2f105fae --- /dev/null +++ b/src/raindrop/types/rehydration_status_create_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo +from .shared_params.liquidmetal_v1alpha1_smart_memory_name import LiquidmetalV1alpha1SmartMemoryName + +__all__ = ["RehydrationStatusCreateParams", "SmartMemoryLocation", "SmartMemoryLocationSmartMemory"] + + +class RehydrationStatusCreateParams(TypedDict, total=False): + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] + """Session identifier to check rehydration status for""" + + smart_memory_location: Required[Annotated[SmartMemoryLocation, PropertyInfo(alias="smartMemoryLocation")]] + """Smart memory locator for targeting the correct smart memory instance""" + + +class SmartMemoryLocationSmartMemory(TypedDict, total=False): + smart_memory: Required[Annotated[LiquidmetalV1alpha1SmartMemoryName, PropertyInfo(alias="smartMemory")]] + """ + **EXAMPLE** {"name":"memory-name","application_name":"demo","version":"1234"} + **REQUIRED** FALSE + """ + + +SmartMemoryLocation: TypeAlias = Union[SmartMemoryLocationSmartMemory, object] diff --git a/src/raindrop/types/rehydration_status_create_response.py b/src/raindrop/types/rehydration_status_create_response.py new file mode 100644 index 00000000..95b02812 --- /dev/null +++ b/src/raindrop/types/rehydration_status_create_response.py @@ -0,0 +1,29 @@ +# 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 + +__all__ = ["RehydrationStatusCreateResponse"] + + +class RehydrationStatusCreateResponse(BaseModel): + completed_at: Optional[str] = FieldInfo(alias="completedAt", default=None) + """When the rehydration operation completed""" + + entries_restored: Optional[int] = FieldInfo(alias="entriesRestored", default=None) + """Number of entries restored (available when completed)""" + + error: Optional[str] = None + """Error message if the operation failed""" + + initiated_at: Optional[str] = FieldInfo(alias="initiatedAt", default=None) + """When the rehydration operation was initiated""" + + session_id: Optional[str] = FieldInfo(alias="sessionId", default=None) + """The session being rehydrated""" + + status: Optional[str] = None + """Current status: 'none', 'initiated', 'processing', 'completed', or 'failed'""" diff --git a/src/raindrop/types/shared/liquidmetal_v1alpha1_smart_memory_name.py b/src/raindrop/types/shared/liquidmetal_v1alpha1_smart_memory_name.py index 5a9d6d4a..8a5d60b4 100644 --- a/src/raindrop/types/shared/liquidmetal_v1alpha1_smart_memory_name.py +++ b/src/raindrop/types/shared/liquidmetal_v1alpha1_smart_memory_name.py @@ -10,6 +10,8 @@ class LiquidmetalV1alpha1SmartMemoryName(BaseModel): + """SmartMemoryName represents a smart memory name with an optional version""" + application_name: Optional[str] = FieldInfo(alias="applicationName", default=None) """Optional Application **EXAMPLE** "my-app" **REQUIRED** TRUE""" diff --git a/src/raindrop/types/shared_params/liquidmetal_v1alpha1_smart_memory_name.py b/src/raindrop/types/shared_params/liquidmetal_v1alpha1_smart_memory_name.py index 600b43bd..2430d638 100644 --- a/src/raindrop/types/shared_params/liquidmetal_v1alpha1_smart_memory_name.py +++ b/src/raindrop/types/shared_params/liquidmetal_v1alpha1_smart_memory_name.py @@ -11,6 +11,8 @@ class LiquidmetalV1alpha1SmartMemoryName(TypedDict, total=False): + """SmartMemoryName represents a smart memory name with an optional version""" + application_name: Required[Annotated[Optional[str], PropertyInfo(alias="applicationName")]] """Optional Application **EXAMPLE** "my-app" **REQUIRED** TRUE""" diff --git a/src/raindrop/types/update_metadata_update_params.py b/src/raindrop/types/update_metadata_update_params.py new file mode 100644 index 00000000..f8497af5 --- /dev/null +++ b/src/raindrop/types/update_metadata_update_params.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "UpdateMetadataUpdateParams", + "SmartSqlLocation", + "SmartSqlLocationSmartSql", + "SmartSqlLocationSmartSqlSmartSql", + "Table", + "TableColumn", +] + + +class UpdateMetadataUpdateParams(TypedDict, total=False): + smart_sql_location: Required[Annotated[SmartSqlLocation, PropertyInfo(alias="smartSqlLocation")]] + """Smart SQL locator for targeting the correct smart SQL instance""" + + tables: Required[Iterable[Table]] + """Table metadata to update or create""" + + mode: Optional[Literal["UPDATE_MODE_UNSPECIFIED", "UPDATE_MODE_REPLACE", "UPDATE_MODE_MERGE", "UPDATE_MODE_APPEND"]] + """ + Update mode: replace (overwrite), merge (preserve existing), or append (only new + entries) + """ + + +class SmartSqlLocationSmartSqlSmartSql(TypedDict, total=False): + """Name-based smart SQL instance identifier (recommended)""" + + name: Required[str] + """The name of the smart SQL instance""" + + application_name: Annotated[Optional[str], PropertyInfo(alias="applicationName")] + """Optional application name that owns this smart SQL instance""" + + version: Optional[str] + """Optional version identifier for the smart SQL instance""" + + +class SmartSqlLocationSmartSql(TypedDict, total=False): + smart_sql: Required[Annotated[SmartSqlLocationSmartSqlSmartSql, PropertyInfo(alias="smartSql")]] + """Name-based smart SQL instance identifier (recommended)""" + + +SmartSqlLocation: TypeAlias = Union[SmartSqlLocationSmartSql, object] + + +class TableColumn(TypedDict, total=False): + column_name: Annotated[str, PropertyInfo(alias="columnName")] + """Name of the database column""" + + data_type: Annotated[str, PropertyInfo(alias="dataType")] + """Data type of the column""" + + is_primary_key: Annotated[bool, PropertyInfo(alias="isPrimaryKey")] + """Whether the column is a primary key""" + + nullable: bool + """Whether the column can contain null values""" + + sample_data: Annotated[Optional[str], PropertyInfo(alias="sampleData")] + """Sample data for AI context (nullable)""" + + +class Table(TypedDict, total=False): + columns: Iterable[TableColumn] + """List of column information for the table""" + + created_at: Annotated[Union[str, datetime, None], PropertyInfo(alias="createdAt", format="iso8601")] + """When this table metadata was created""" + + table_name: Annotated[str, PropertyInfo(alias="tableName")] + """Name of the database table""" + + updated_at: Annotated[Union[str, datetime, None], PropertyInfo(alias="updatedAt", format="iso8601")] + """When this table metadata was last updated""" diff --git a/src/raindrop/types/update_metadata_update_response.py b/src/raindrop/types/update_metadata_update_response.py new file mode 100644 index 00000000..36d99bf0 --- /dev/null +++ b/src/raindrop/types/update_metadata_update_response.py @@ -0,0 +1,17 @@ +# 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 + +__all__ = ["UpdateMetadataUpdateResponse"] + + +class UpdateMetadataUpdateResponse(BaseModel): + success: Optional[bool] = None + """Indicates whether the update was successful""" + + tables_updated: Optional[int] = FieldInfo(alias="tablesUpdated", default=None) + """Number of tables updated""" diff --git a/tests/api_resources/bucket/__init__.py b/tests/api_resources/bucket/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/bucket/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/bucket/test_by_status.py b/tests/api_resources/bucket/test_by_status.py new file mode 100644 index 00000000..b41af3e9 --- /dev/null +++ b/tests/api_resources/bucket/test_by_status.py @@ -0,0 +1,170 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types.bucket import ByStatusListObjectsResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestByStatus: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_objects(self, client: Raindrop) -> None: + by_status = client.bucket.by_status.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_objects_with_all_params(self, client: Raindrop) -> None: + by_status = client.bucket.by_status.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + exclude=True, + partition="default", + prefix="documents/", + ) + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_objects(self, client: Raindrop) -> None: + response = client.bucket.by_status.with_raw_response.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + by_status = response.parse() + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_objects(self, client: Raindrop) -> None: + with client.bucket.by_status.with_streaming_response.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + by_status = response.parse() + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncByStatus: + 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_list_objects(self, async_client: AsyncRaindrop) -> None: + by_status = await async_client.bucket.by_status.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_objects_with_all_params(self, async_client: AsyncRaindrop) -> None: + by_status = await async_client.bucket.by_status.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + exclude=True, + partition="default", + prefix="documents/", + ) + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_objects(self, async_client: AsyncRaindrop) -> None: + response = await async_client.bucket.by_status.with_raw_response.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + by_status = await response.parse() + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_objects(self, async_client: AsyncRaindrop) -> None: + async with async_client.bucket.by_status.with_streaming_response.list_objects( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + statuses=["failed", "processing"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + by_status = await response.parse() + assert_matches_type(ByStatusListObjectsResponse, by_status, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/query/test_episodic_memory.py b/tests/api_resources/query/test_episodic_memory.py index 3fb1cd4c..a32fecda 100644 --- a/tests/api_resources/query/test_episodic_memory.py +++ b/tests/api_resources/query/test_episodic_memory.py @@ -45,9 +45,9 @@ def test_method_search_with_all_params(self, client: Raindrop) -> None: } }, terms="sessions about user interface preferences", - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), ) assert_matches_type(EpisodicMemorySearchResponse, episodic_memory, path=["response"]) @@ -124,9 +124,9 @@ async def test_method_search_with_all_params(self, async_client: AsyncRaindrop) } }, terms="sessions about user interface preferences", - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), ) assert_matches_type(EpisodicMemorySearchResponse, episodic_memory, path=["response"]) diff --git a/tests/api_resources/query/test_memory.py b/tests/api_resources/query/test_memory.py index 8872269f..542f67a2 100644 --- a/tests/api_resources/query/test_memory.py +++ b/tests/api_resources/query/test_memory.py @@ -47,9 +47,9 @@ def test_method_search_with_all_params(self, client: Raindrop) -> None: } }, terms="user interface preferences", - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), timeline="user-conversation-2024", ) assert_matches_type(MemorySearchResponse, memory, path=["response"]) @@ -131,9 +131,9 @@ async def test_method_search_with_all_params(self, async_client: AsyncRaindrop) } }, terms="user interface preferences", - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), timeline="user-conversation-2024", ) assert_matches_type(MemorySearchResponse, memory, path=["response"]) diff --git a/tests/api_resources/test_bucket.py b/tests/api_resources/test_bucket.py index b489b723..e300bf19 100644 --- a/tests/api_resources/test_bucket.py +++ b/tests/api_resources/test_bucket.py @@ -25,7 +25,28 @@ class TestBucket: @parametrize def test_method_list(self, client: Raindrop) -> None: bucket = client.bucket.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + ) + assert_matches_type(BucketListResponse, bucket, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Raindrop) -> None: + bucket = client.bucket.list( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + prefix="documents/", ) assert_matches_type(BucketListResponse, bucket, path=["response"]) @@ -33,7 +54,13 @@ def test_method_list(self, client: Raindrop) -> None: @parametrize def test_raw_response_list(self, client: Raindrop) -> None: response = client.bucket.with_raw_response.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, ) assert response.is_closed is True @@ -45,7 +72,13 @@ def test_raw_response_list(self, client: Raindrop) -> None: @parametrize def test_streaming_response_list(self, client: Raindrop) -> None: with client.bucket.with_streaming_response.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -59,7 +92,13 @@ def test_streaming_response_list(self, client: Raindrop) -> None: @parametrize def test_method_delete(self, client: Raindrop) -> None: bucket = client.bucket.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) assert_matches_type(object, bucket, path=["response"]) @@ -68,7 +107,13 @@ def test_method_delete(self, client: Raindrop) -> None: @parametrize def test_raw_response_delete(self, client: Raindrop) -> None: response = client.bucket.with_raw_response.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) @@ -81,7 +126,13 @@ def test_raw_response_delete(self, client: Raindrop) -> None: @parametrize def test_streaming_response_delete(self, client: Raindrop) -> None: with client.bucket.with_streaming_response.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) as response: assert not response.is_closed @@ -96,7 +147,13 @@ def test_streaming_response_delete(self, client: Raindrop) -> None: @parametrize def test_method_get(self, client: Raindrop) -> None: bucket = client.bucket.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) assert_matches_type(BucketGetResponse, bucket, path=["response"]) @@ -105,7 +162,13 @@ def test_method_get(self, client: Raindrop) -> None: @parametrize def test_raw_response_get(self, client: Raindrop) -> None: response = client.bucket.with_raw_response.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) @@ -118,7 +181,13 @@ def test_raw_response_get(self, client: Raindrop) -> None: @parametrize def test_streaming_response_get(self, client: Raindrop) -> None: with client.bucket.with_streaming_response.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) as response: assert not response.is_closed @@ -133,7 +202,13 @@ def test_streaming_response_get(self, client: Raindrop) -> None: @parametrize def test_method_put(self, client: Raindrop) -> None: bucket = client.bucket.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", @@ -144,7 +219,13 @@ def test_method_put(self, client: Raindrop) -> None: @parametrize def test_raw_response_put(self, client: Raindrop) -> None: response = client.bucket.with_raw_response.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", @@ -159,7 +240,13 @@ def test_raw_response_put(self, client: Raindrop) -> None: @parametrize def test_streaming_response_put(self, client: Raindrop) -> None: with client.bucket.with_streaming_response.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", @@ -182,7 +269,28 @@ class TestAsyncBucket: @parametrize async def test_method_list(self, async_client: AsyncRaindrop) -> None: bucket = await async_client.bucket.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + ) + assert_matches_type(BucketListResponse, bucket, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncRaindrop) -> None: + bucket = await async_client.bucket.list( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + prefix="documents/", ) assert_matches_type(BucketListResponse, bucket, path=["response"]) @@ -190,7 +298,13 @@ async def test_method_list(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_raw_response_list(self, async_client: AsyncRaindrop) -> None: response = await async_client.bucket.with_raw_response.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, ) assert response.is_closed is True @@ -202,7 +316,13 @@ async def test_raw_response_list(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncRaindrop) -> None: async with async_client.bucket.with_streaming_response.list( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -216,7 +336,13 @@ async def test_streaming_response_list(self, async_client: AsyncRaindrop) -> Non @parametrize async def test_method_delete(self, async_client: AsyncRaindrop) -> None: bucket = await async_client.bucket.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) assert_matches_type(object, bucket, path=["response"]) @@ -225,7 +351,13 @@ async def test_method_delete(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_raw_response_delete(self, async_client: AsyncRaindrop) -> None: response = await async_client.bucket.with_raw_response.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) @@ -238,7 +370,13 @@ async def test_raw_response_delete(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncRaindrop) -> None: async with async_client.bucket.with_streaming_response.delete( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) as response: assert not response.is_closed @@ -253,7 +391,13 @@ async def test_streaming_response_delete(self, async_client: AsyncRaindrop) -> N @parametrize async def test_method_get(self, async_client: AsyncRaindrop) -> None: bucket = await async_client.bucket.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) assert_matches_type(BucketGetResponse, bucket, path=["response"]) @@ -262,7 +406,13 @@ async def test_method_get(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_raw_response_get(self, async_client: AsyncRaindrop) -> None: response = await async_client.bucket.with_raw_response.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) @@ -275,7 +425,13 @@ async def test_raw_response_get(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_streaming_response_get(self, async_client: AsyncRaindrop) -> None: async with async_client.bucket.with_streaming_response.get( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, key="my-key", ) as response: assert not response.is_closed @@ -290,7 +446,13 @@ async def test_streaming_response_get(self, async_client: AsyncRaindrop) -> None @parametrize async def test_method_put(self, async_client: AsyncRaindrop) -> None: bucket = await async_client.bucket.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", @@ -301,7 +463,13 @@ async def test_method_put(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_raw_response_put(self, async_client: AsyncRaindrop) -> None: response = await async_client.bucket.with_raw_response.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", @@ -316,7 +484,13 @@ async def test_raw_response_put(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_streaming_response_put(self, async_client: AsyncRaindrop) -> None: async with async_client.bucket.with_streaming_response.put( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, content="U3RhaW5sZXNzIHJvY2tz", content_type="application/pdf", key="my-key", diff --git a/tests/api_resources/test_document_status.py b/tests/api_resources/test_document_status.py new file mode 100644 index 00000000..4ec0bd14 --- /dev/null +++ b/tests/api_resources/test_document_status.py @@ -0,0 +1,166 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import DocumentStatusGetStatusResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDocumentStatus: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_status(self, client: Raindrop) -> None: + document_status = client.document_status.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_status_with_all_params(self, client: Raindrop) -> None: + document_status = client.document_status.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + partition="tenant-123", + ) + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get_status(self, client: Raindrop) -> None: + response = client.document_status.with_raw_response.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document_status = response.parse() + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_get_status(self, client: Raindrop) -> None: + with client.document_status.with_streaming_response.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document_status = response.parse() + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDocumentStatus: + 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_get_status(self, async_client: AsyncRaindrop) -> None: + document_status = await async_client.document_status.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get_status_with_all_params(self, async_client: AsyncRaindrop) -> None: + document_status = await async_client.document_status.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + partition="tenant-123", + ) + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get_status(self, async_client: AsyncRaindrop) -> None: + response = await async_client.document_status.with_raw_response.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document_status = await response.parse() + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get_status(self, async_client: AsyncRaindrop) -> None: + async with async_client.document_status.with_streaming_response.get_status( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_id="document.pdf", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document_status = await response.parse() + assert_matches_type(DocumentStatusGetStatusResponse, document_status, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_document_status_bulk.py b/tests/api_resources/test_document_status_bulk.py new file mode 100644 index 00000000..4589800f --- /dev/null +++ b/tests/api_resources/test_document_status_bulk.py @@ -0,0 +1,168 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import ( + DocumentStatusBulkGetStatusBulkResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDocumentStatusBulk: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_status_bulk(self, client: Raindrop) -> None: + document_status_bulk = client.document_status_bulk.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_status_bulk_with_all_params(self, client: Raindrop) -> None: + document_status_bulk = client.document_status_bulk.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + partition="tenant-123", + ) + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get_status_bulk(self, client: Raindrop) -> None: + response = client.document_status_bulk.with_raw_response.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document_status_bulk = response.parse() + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_get_status_bulk(self, client: Raindrop) -> None: + with client.document_status_bulk.with_streaming_response.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document_status_bulk = response.parse() + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDocumentStatusBulk: + 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_get_status_bulk(self, async_client: AsyncRaindrop) -> None: + document_status_bulk = await async_client.document_status_bulk.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get_status_bulk_with_all_params(self, async_client: AsyncRaindrop) -> None: + document_status_bulk = await async_client.document_status_bulk.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + partition="tenant-123", + ) + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get_status_bulk(self, async_client: AsyncRaindrop) -> None: + response = await async_client.document_status_bulk.with_raw_response.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document_status_bulk = await response.parse() + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get_status_bulk(self, async_client: AsyncRaindrop) -> None: + async with async_client.document_status_bulk.with_streaming_response.get_status_bulk( + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, + object_ids=["document1.pdf", "document2.pdf", "document3.pdf"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document_status_bulk = await response.parse() + assert_matches_type(DocumentStatusBulkGetStatusBulkResponse, document_status_bulk, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_execute_query.py b/tests/api_resources/test_execute_query.py new file mode 100644 index 00000000..02ae78e1 --- /dev/null +++ b/tests/api_resources/test_execute_query.py @@ -0,0 +1,126 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import ExecuteQueryExecuteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExecuteQuery: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_execute(self, client: Raindrop) -> None: + execute_query = client.execute_query.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_execute_with_all_params(self, client: Raindrop) -> None: + execute_query = client.execute_query.execute( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + format="OUTPUT_FORMAT_UNSPECIFIED", + sql_query="SELECT * FROM users WHERE active = true", + text_query="Show me all active users from the last month", + ) + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_execute(self, client: Raindrop) -> None: + response = client.execute_query.with_raw_response.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + execute_query = response.parse() + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_execute(self, client: Raindrop) -> None: + with client.execute_query.with_streaming_response.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + execute_query = response.parse() + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncExecuteQuery: + 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_execute(self, async_client: AsyncRaindrop) -> None: + execute_query = await async_client.execute_query.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_execute_with_all_params(self, async_client: AsyncRaindrop) -> None: + execute_query = await async_client.execute_query.execute( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + format="OUTPUT_FORMAT_UNSPECIFIED", + sql_query="SELECT * FROM users WHERE active = true", + text_query="Show me all active users from the last month", + ) + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_execute(self, async_client: AsyncRaindrop) -> None: + response = await async_client.execute_query.with_raw_response.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + execute_query = await response.parse() + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_execute(self, async_client: AsyncRaindrop) -> None: + async with async_client.execute_query.with_streaming_response.execute( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + execute_query = await response.parse() + assert_matches_type(ExecuteQueryExecuteResponse, execute_query, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_get_memory.py b/tests/api_resources/test_get_memory.py index 4f66cc15..5e7763ac 100644 --- a/tests/api_resources/test_get_memory.py +++ b/tests/api_resources/test_get_memory.py @@ -45,10 +45,10 @@ def test_method_retrieve_with_all_params(self, client: Raindrop) -> None: "version": "1234", } }, - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), key="user-preference-theme", n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), timeline="user-conversation-2024", ) assert_matches_type(GetMemoryRetrieveResponse, get_memory, path=["response"]) @@ -126,10 +126,10 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncRaindrop "version": "1234", } }, - end_time=parse_datetime("2019-12-27T18:11:19.117Z"), + end_time=parse_datetime("2023-01-15T01:30:15.01Z"), key="user-preference-theme", n_most_recent=10, - start_time=parse_datetime("2019-12-27T18:11:19.117Z"), + start_time=parse_datetime("2023-01-15T01:30:15.01Z"), timeline="user-conversation-2024", ) assert_matches_type(GetMemoryRetrieveResponse, get_memory, path=["response"]) diff --git a/tests/api_resources/test_get_metadata.py b/tests/api_resources/test_get_metadata.py new file mode 100644 index 00000000..b5ad12c9 --- /dev/null +++ b/tests/api_resources/test_get_metadata.py @@ -0,0 +1,122 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import GetMetadataRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGetMetadata: + 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: Raindrop) -> None: + get_metadata = client.get_metadata.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Raindrop) -> None: + get_metadata = client.get_metadata.retrieve( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + table_name="users", + ) + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Raindrop) -> None: + response = client.get_metadata.with_raw_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + get_metadata = response.parse() + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Raindrop) -> None: + with client.get_metadata.with_streaming_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + get_metadata = response.parse() + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGetMetadata: + 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: AsyncRaindrop) -> None: + get_metadata = await async_client.get_metadata.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncRaindrop) -> None: + get_metadata = await async_client.get_metadata.retrieve( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + table_name="users", + ) + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncRaindrop) -> None: + response = await async_client.get_metadata.with_raw_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + get_metadata = await response.parse() + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncRaindrop) -> None: + async with async_client.get_metadata.with_streaming_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + get_metadata = await response.parse() + assert_matches_type(GetMetadataRetrieveResponse, get_metadata, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_get_pii_data.py b/tests/api_resources/test_get_pii_data.py new file mode 100644 index 00000000..c41d6ef3 --- /dev/null +++ b/tests/api_resources/test_get_pii_data.py @@ -0,0 +1,130 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import GetPiiDataRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGetPiiData: + 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: Raindrop) -> None: + get_pii_data = client.get_pii_data.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Raindrop) -> None: + get_pii_data = client.get_pii_data.retrieve( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + table_name="users", + record_id="user_123", + ) + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Raindrop) -> None: + response = client.get_pii_data.with_raw_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + get_pii_data = response.parse() + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Raindrop) -> None: + with client.get_pii_data.with_streaming_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + get_pii_data = response.parse() + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGetPiiData: + 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: AsyncRaindrop) -> None: + get_pii_data = await async_client.get_pii_data.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncRaindrop) -> None: + get_pii_data = await async_client.get_pii_data.retrieve( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + table_name="users", + record_id="user_123", + ) + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncRaindrop) -> None: + response = await async_client.get_pii_data.with_raw_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + get_pii_data = await response.parse() + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncRaindrop) -> None: + async with async_client.get_pii_data.with_streaming_response.retrieve( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + table_name="users", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + get_pii_data = await response.parse() + assert_matches_type(GetPiiDataRetrieveResponse, get_pii_data, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_put_memory.py b/tests/api_resources/test_put_memory.py index 995a542a..300d687b 100644 --- a/tests/api_resources/test_put_memory.py +++ b/tests/api_resources/test_put_memory.py @@ -9,7 +9,10 @@ from raindrop import Raindrop, AsyncRaindrop from tests.utils import assert_matches_type -from raindrop.types import PutMemoryCreateResponse +from raindrop.types import ( + PutMemoryCreateResponse, + PutMemoryCreateBatchResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -94,6 +97,64 @@ def test_streaming_response_create(self, client: Raindrop) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_batch(self, client: Raindrop) -> None: + put_memory = client.put_memory.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create_batch(self, client: Raindrop) -> None: + response = client.put_memory.with_raw_response.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + put_memory = response.parse() + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create_batch(self, client: Raindrop) -> None: + with client.put_memory.with_streaming_response.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + put_memory = response.parse() + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncPutMemory: parametrize = pytest.mark.parametrize( @@ -176,3 +237,61 @@ async def test_streaming_response_create(self, async_client: AsyncRaindrop) -> N assert_matches_type(PutMemoryCreateResponse, put_memory, path=["response"]) assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_batch(self, async_client: AsyncRaindrop) -> None: + put_memory = await async_client.put_memory.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create_batch(self, async_client: AsyncRaindrop) -> None: + response = await async_client.put_memory.with_raw_response.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + put_memory = await response.parse() + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create_batch(self, async_client: AsyncRaindrop) -> None: + async with async_client.put_memory.with_streaming_response.create_batch( + entries=[{"content": "User prefers dark theme for the interface"}], + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + put_memory = await response.parse() + assert_matches_type(PutMemoryCreateBatchResponse, put_memory, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_query.py b/tests/api_resources/test_query.py index cfcc44de..3d041907 100644 --- a/tests/api_resources/test_query.py +++ b/tests/api_resources/test_query.py @@ -28,7 +28,15 @@ class TestQuery: @parametrize def test_method_chunk_search(self, client: Raindrop) -> None: query = client.query.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) @@ -42,8 +50,8 @@ def test_method_chunk_search_with_all_params(self, client: Raindrop) -> None: { "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } } ], @@ -57,7 +65,15 @@ def test_method_chunk_search_with_all_params(self, client: Raindrop) -> None: @parametrize def test_raw_response_chunk_search(self, client: Raindrop) -> None: response = client.query.with_raw_response.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) @@ -71,7 +87,15 @@ def test_raw_response_chunk_search(self, client: Raindrop) -> None: @parametrize def test_streaming_response_chunk_search(self, client: Raindrop) -> None: with client.query.with_streaming_response.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) as response: @@ -87,7 +111,13 @@ def test_streaming_response_chunk_search(self, client: Raindrop) -> None: @parametrize def test_method_document_query(self, client: Raindrop) -> None: query = client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -101,8 +131,8 @@ def test_method_document_query_with_all_params(self, client: Raindrop) -> None: bucket_location={ "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } }, input="What are the key points in this document?", @@ -116,7 +146,13 @@ def test_method_document_query_with_all_params(self, client: Raindrop) -> None: @parametrize def test_raw_response_document_query(self, client: Raindrop) -> None: response = client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -131,7 +167,13 @@ def test_raw_response_document_query(self, client: Raindrop) -> None: @parametrize def test_streaming_response_document_query(self, client: Raindrop) -> None: with client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -199,7 +241,15 @@ def test_streaming_response_get_paginated_search(self, client: Raindrop) -> None @parametrize def test_method_search(self, client: Raindrop) -> None: query = client.query.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) @@ -213,8 +263,8 @@ def test_method_search_with_all_params(self, client: Raindrop) -> None: { "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } } ], @@ -228,7 +278,15 @@ def test_method_search_with_all_params(self, client: Raindrop) -> None: @parametrize def test_raw_response_search(self, client: Raindrop) -> None: response = client.query.with_raw_response.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) @@ -242,7 +300,15 @@ def test_raw_response_search(self, client: Raindrop) -> None: @parametrize def test_streaming_response_search(self, client: Raindrop) -> None: with client.query.with_streaming_response.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) as response: @@ -315,7 +381,15 @@ class TestAsyncQuery: @parametrize async def test_method_chunk_search(self, async_client: AsyncRaindrop) -> None: query = await async_client.query.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) @@ -329,8 +403,8 @@ async def test_method_chunk_search_with_all_params(self, async_client: AsyncRain { "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } } ], @@ -344,7 +418,15 @@ async def test_method_chunk_search_with_all_params(self, async_client: AsyncRain @parametrize async def test_raw_response_chunk_search(self, async_client: AsyncRaindrop) -> None: response = await async_client.query.with_raw_response.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) @@ -358,7 +440,15 @@ async def test_raw_response_chunk_search(self, async_client: AsyncRaindrop) -> N @parametrize async def test_streaming_response_chunk_search(self, async_client: AsyncRaindrop) -> None: async with async_client.query.with_streaming_response.chunk_search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="Find documents about revenue in Q4 2023", request_id="", ) as response: @@ -374,7 +464,13 @@ async def test_streaming_response_chunk_search(self, async_client: AsyncRaindrop @parametrize async def test_method_document_query(self, async_client: AsyncRaindrop) -> None: query = await async_client.query.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -388,8 +484,8 @@ async def test_method_document_query_with_all_params(self, async_client: AsyncRa bucket_location={ "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } }, input="What are the key points in this document?", @@ -403,7 +499,13 @@ async def test_method_document_query_with_all_params(self, async_client: AsyncRa @parametrize async def test_raw_response_document_query(self, async_client: AsyncRaindrop) -> None: response = await async_client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -418,7 +520,13 @@ async def test_raw_response_document_query(self, async_client: AsyncRaindrop) -> @parametrize async def test_streaming_response_document_query(self, async_client: AsyncRaindrop) -> None: async with async_client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -486,7 +594,15 @@ async def test_streaming_response_get_paginated_search(self, async_client: Async @parametrize async def test_method_search(self, async_client: AsyncRaindrop) -> None: query = await async_client.query.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) @@ -500,8 +616,8 @@ async def test_method_search_with_all_params(self, async_client: AsyncRaindrop) { "bucket": { "name": "my-smartbucket", - "application_name": "my-app", "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", } } ], @@ -515,7 +631,15 @@ async def test_method_search_with_all_params(self, async_client: AsyncRaindrop) @parametrize async def test_raw_response_search(self, async_client: AsyncRaindrop) -> None: response = await async_client.query.with_raw_response.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) @@ -529,7 +653,15 @@ async def test_raw_response_search(self, async_client: AsyncRaindrop) -> None: @parametrize async def test_streaming_response_search(self, async_client: AsyncRaindrop) -> None: async with async_client.query.with_streaming_response.search( - bucket_locations=[{"bucket": {"name": "my-smartbucket"}}], + bucket_locations=[ + { + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + } + ], input="All my files", request_id="", ) as response: diff --git a/tests/api_resources/test_rehydration_status.py b/tests/api_resources/test_rehydration_status.py new file mode 100644 index 00000000..e3b99e67 --- /dev/null +++ b/tests/api_resources/test_rehydration_status.py @@ -0,0 +1,134 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import RehydrationStatusCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRehydrationStatus: + 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(self, client: Raindrop) -> None: + rehydration_status = client.rehydration_status.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Raindrop) -> None: + response = client.rehydration_status.with_raw_response.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rehydration_status = response.parse() + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Raindrop) -> None: + with client.rehydration_status.with_streaming_response.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rehydration_status = response.parse() + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncRehydrationStatus: + 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(self, async_client: AsyncRaindrop) -> None: + rehydration_status = await async_client.rehydration_status.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncRaindrop) -> None: + response = await async_client.rehydration_status.with_raw_response.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rehydration_status = await response.parse() + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncRaindrop) -> None: + async with async_client.rehydration_status.with_streaming_response.create( + session_id="01jxanr45haeswhay4n0q8340y", + smart_memory_location={ + "smartMemory": { + "name": "memory-name", + "application_name": "demo", + "version": "1234", + } + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rehydration_status = await response.parse() + assert_matches_type(RehydrationStatusCreateResponse, rehydration_status, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_update_metadata.py b/tests/api_resources/test_update_metadata.py new file mode 100644 index 00000000..e87c18f6 --- /dev/null +++ b/tests/api_resources/test_update_metadata.py @@ -0,0 +1,161 @@ +# 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 raindrop import Raindrop, AsyncRaindrop +from tests.utils import assert_matches_type +from raindrop.types import UpdateMetadataUpdateResponse +from raindrop._utils import parse_datetime + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUpdateMetadata: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Raindrop) -> None: + update_metadata = client.update_metadata.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Raindrop) -> None: + update_metadata = client.update_metadata.update( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + tables=[ + { + "columns": [ + { + "column_name": "user_id", + "data_type": "INTEGER", + "is_primary_key": True, + "nullable": True, + "sample_data": "123", + } + ], + "created_at": parse_datetime("2023-01-15T01:30:15.01Z"), + "table_name": "users", + "updated_at": parse_datetime("2023-01-15T01:30:15.01Z"), + } + ], + mode="UPDATE_MODE_MERGE", + ) + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Raindrop) -> None: + response = client.update_metadata.with_raw_response.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + update_metadata = response.parse() + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Raindrop) -> None: + with client.update_metadata.with_streaming_response.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + update_metadata = response.parse() + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncUpdateMetadata: + 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_update(self, async_client: AsyncRaindrop) -> None: + update_metadata = await async_client.update_metadata.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncRaindrop) -> None: + update_metadata = await async_client.update_metadata.update( + smart_sql_location={ + "smart_sql": { + "name": "analytics-sql", + "application_name": "data-analytics-app", + "version": "v1.2.0", + } + }, + tables=[ + { + "columns": [ + { + "column_name": "user_id", + "data_type": "INTEGER", + "is_primary_key": True, + "nullable": True, + "sample_data": "123", + } + ], + "created_at": parse_datetime("2023-01-15T01:30:15.01Z"), + "table_name": "users", + "updated_at": parse_datetime("2023-01-15T01:30:15.01Z"), + } + ], + mode="UPDATE_MODE_MERGE", + ) + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncRaindrop) -> None: + response = await async_client.update_metadata.with_raw_response.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + update_metadata = await response.parse() + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncRaindrop) -> None: + async with async_client.update_metadata.with_streaming_response.update( + smart_sql_location={"smart_sql": {"name": "analytics-sql"}}, + tables=[{}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + update_metadata = await response.parse() + assert_matches_type(UpdateMetadataUpdateResponse, update_metadata, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/test_client.py b/tests/test_client.py index fc95ac39..44bc0a77 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -8,10 +8,11 @@ import json import asyncio import inspect +import dataclasses import tracemalloc -from typing import Any, Union, cast +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock -from typing_extensions import Literal +from typing_extensions import Literal, AsyncIterator, override import httpx import pytest @@ -36,6 +37,7 @@ from .utils import update_env +T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" @@ -50,6 +52,57 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 +def mirror_request_content(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=request.content) + + +# note: we can't use the httpx.MockTransport class as it consumes the request +# body itself, which means we can't test that the body is read lazily +class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): + def __init__( + self, + handler: Callable[[httpx.Request], httpx.Response] + | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], + ) -> None: + self.handler = handler + + @override + def handle_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" + assert inspect.isfunction(self.handler), "handler must be a function" + return self.handler(request) + + @override + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" + return await self.handler(request) + + +@dataclasses.dataclass +class Counter: + value: int = 0 + + +def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + def _get_open_connections(client: Raindrop | AsyncRaindrop) -> int: transport = client._client._transport assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) @@ -59,51 +112,49 @@ def _get_open_connections(client: Raindrop | AsyncRaindrop) -> int: class TestRaindrop: - client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: Raindrop) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Raindrop) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: Raindrop) -> None: + copied = client.copy() + assert id(copied) != id(client) - copied = self.client.copy(api_key="another My API Key") + copied = client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: Raindrop) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = Raindrop( @@ -138,6 +189,7 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = Raindrop( @@ -175,13 +227,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: Raindrop) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -192,12 +246,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, client: Raindrop) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -254,14 +308,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: Raindrop) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) @@ -274,6 +326,8 @@ def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: @@ -285,6 +339,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Raindrop( @@ -295,6 +351,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Raindrop( @@ -305,6 +363,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: @@ -316,14 +376,14 @@ async def test_invalid_http_client(self) -> None: ) def test_default_headers_option(self) -> None: - client = Raindrop( + test_client = Raindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = Raindrop( + test_client2 = Raindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -332,10 +392,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + test_client.close() + test_client2.close() + def test_default_query_option(self) -> None: client = Raindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} @@ -354,8 +417,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -366,7 +431,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -377,7 +442,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -388,8 +453,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -399,7 +464,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -410,8 +475,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -424,7 +489,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -438,7 +503,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -481,7 +546,71 @@ def test_multipart_repeating_array(self, client: Raindrop) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_binary_content_upload(self, respx_mock: MockRouter, client: Raindrop) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + def test_binary_content_upload_with_iterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_sync_iterator([file_content], counter=counter) + + def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=request.read()) + + with Raindrop( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), + ) as client: + response = client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Raindrop) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter, client: Raindrop) -> None: class Model1(BaseModel): name: str @@ -490,12 +619,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: Raindrop) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -506,18 +635,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Raindrop) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -533,7 +662,7 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 @@ -545,6 +674,8 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(RAINDROP_BASE_URL="http://localhost:5000/from/env"): client = Raindrop(api_key=api_key, _strict_response_validation=True) @@ -572,6 +703,7 @@ def test_base_url_trailing_slash(self, client: Raindrop) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -595,6 +727,7 @@ def test_base_url_no_trailing_slash(self, client: Raindrop) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -618,35 +751,36 @@ def test_absolute_request_url(self, client: Raindrop) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - with client as c2: - assert c2 is client + test_client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Raindrop) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -666,11 +800,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -693,9 +830,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = Raindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Raindrop + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -708,13 +845,19 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien with pytest.raises(APITimeoutError): client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", ).__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -723,12 +866,18 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client with pytest.raises(APIStatusError): client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", ).__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -757,7 +906,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -786,7 +941,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -815,7 +976,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -847,83 +1014,77 @@ def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - def test_follow_redirects(self, respx_mock: MockRouter) -> None: + def test_follow_redirects(self, respx_mock: MockRouter, client: Raindrop) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Raindrop) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - self.client.post( - "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response - ) + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" class TestAsyncRaindrop: - client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncRaindrop) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - copied = self.client.copy(api_key="another My API Key") + copied = async_client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert async_client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncRaindrop) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncRaindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) @@ -956,8 +1117,9 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncRaindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) @@ -993,13 +1155,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncRaindrop) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -1010,12 +1174,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, async_client: AsyncRaindrop) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1072,12 +1236,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncRaindrop) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1092,6 +1256,8 @@ async def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: @@ -1103,6 +1269,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncRaindrop( @@ -1113,6 +1281,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncRaindrop( @@ -1123,6 +1293,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: @@ -1133,15 +1305,15 @@ def test_invalid_http_client(self) -> None: http_client=cast(Any, http_client), ) - def test_default_headers_option(self) -> None: - client = AsyncRaindrop( + async def test_default_headers_option(self) -> None: + test_client = AsyncRaindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncRaindrop( + test_client2 = AsyncRaindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -1150,11 +1322,14 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" - def test_default_query_option(self) -> None: + await test_client.close() + await test_client2.close() + + async def test_default_query_option(self) -> None: client = AsyncRaindrop( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) @@ -1172,8 +1347,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1184,7 +1361,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1195,7 +1372,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1206,8 +1383,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1217,7 +1394,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1228,8 +1405,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Raindrop) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1242,7 +1419,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1256,7 +1433,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1299,7 +1476,73 @@ def test_multipart_repeating_array(self, async_client: AsyncRaindrop) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = await async_client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + async def test_binary_content_upload_with_asynciterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_async_iterator([file_content], counter=counter) + + async def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=await request.aread()) + + async with AsyncRaindrop( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), + ) as client: + response = await client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload_with_body_is_deprecated( + self, respx_mock: MockRouter, async_client: AsyncRaindrop + ) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = await async_client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: class Model1(BaseModel): name: str @@ -1308,12 +1551,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1324,18 +1567,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncRaindrop + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1351,11 +1596,11 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: + async def test_base_url_setter(self) -> None: client = AsyncRaindrop( base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True ) @@ -1365,7 +1610,9 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(RAINDROP_BASE_URL="http://localhost:5000/from/env"): client = AsyncRaindrop(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @@ -1385,7 +1632,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncRaindrop) -> None: + async def test_base_url_trailing_slash(self, client: AsyncRaindrop) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1394,6 +1641,7 @@ def test_base_url_trailing_slash(self, client: AsyncRaindrop) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1410,7 +1658,7 @@ def test_base_url_trailing_slash(self, client: AsyncRaindrop) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncRaindrop) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncRaindrop) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1419,6 +1667,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncRaindrop) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1435,7 +1684,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncRaindrop) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncRaindrop) -> None: + async def test_absolute_request_url(self, client: AsyncRaindrop) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1444,37 +1693,37 @@ def test_absolute_request_url(self, client: AsyncRaindrop) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - async with client as c2: - assert c2 is client + test_client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -1485,7 +1734,6 @@ async def test_client_max_retries_validation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1497,11 +1745,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1524,13 +1775,12 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncRaindrop(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncRaindrop + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1542,13 +1792,19 @@ async def test_retrying_timeout_errors_doesnt_leak( with pytest.raises(APITimeoutError): await async_client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", ).__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -1559,17 +1815,22 @@ async def test_retrying_status_errors_doesnt_leak( with pytest.raises(APIStatusError): await async_client.query.with_streaming_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", ).__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1594,7 +1855,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = await client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -1606,7 +1873,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( self, async_client: AsyncRaindrop, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1624,7 +1890,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = await client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -1636,7 +1908,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("raindrop._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( self, async_client: AsyncRaindrop, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1654,7 +1925,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v1/document_query").mock(side_effect=retry_handler) response = await client.query.with_raw_response.document_query( - bucket_location={"bucket": {"name": "my-smartbucket"}}, + bucket_location={ + "bucket": { + "name": "my-smartbucket", + "version": "01jxanr45haeswhay4n0q8340y", + "application_name": "my-app", + } + }, input="What are the key points in this document?", object_id="document.pdf", request_id="", @@ -1690,26 +1967,26 @@ async def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncRaindrop) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - await self.client.post( + await async_client.post( "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response ) diff --git a/tests/test_models.py b/tests/test_models.py index 9d97b7af..4150d1d3 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from raindrop._utils import PropertyInfo from raindrop._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from raindrop._models import BaseModel, construct_type +from raindrop._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") diff --git a/tests/test_utils/test_json.py b/tests/test_utils/test_json.py new file mode 100644 index 00000000..e9f0e7f5 --- /dev/null +++ b/tests/test_utils/test_json.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import datetime +from typing import Union + +import pydantic + +from raindrop import _compat +from raindrop._utils._json import openapi_dumps + + +class TestOpenapiDumps: + def test_basic(self) -> None: + data = {"key": "value", "number": 42} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"key":"value","number":42}' + + def test_datetime_serialization(self) -> None: + dt = datetime.datetime(2023, 1, 1, 12, 0, 0) + data = {"datetime": dt} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}' + + def test_pydantic_model_serialization(self) -> None: + class User(pydantic.BaseModel): + first_name: str + last_name: str + age: int + + model_instance = User(first_name="John", last_name="Kramer", age=83) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}' + + def test_pydantic_model_with_default_values(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + score: int = 0 + + model_instance = User(name="Alice") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Alice"}}' + + def test_pydantic_model_with_default_values_overridden(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + + model_instance = User(name="Bob", role="admin", active=False) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}' + + def test_pydantic_model_with_alias(self) -> None: + class User(pydantic.BaseModel): + first_name: str = pydantic.Field(alias="firstName") + last_name: str = pydantic.Field(alias="lastName") + + model_instance = User(firstName="John", lastName="Doe") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}' + + def test_pydantic_model_with_alias_and_default(self) -> None: + class User(pydantic.BaseModel): + user_name: str = pydantic.Field(alias="userName") + user_role: str = pydantic.Field(default="member", alias="userRole") + is_active: bool = pydantic.Field(default=True, alias="isActive") + + model_instance = User(userName="charlie") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"charlie"}}' + + model_with_overrides = User(userName="diana", userRole="admin", isActive=False) + data = {"model": model_with_overrides} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}' + + def test_pydantic_model_with_nested_models_and_defaults(self) -> None: + class Address(pydantic.BaseModel): + street: str + city: str = "Unknown" + + class User(pydantic.BaseModel): + name: str + address: Address + verified: bool = False + + if _compat.PYDANTIC_V1: + # to handle forward references in Pydantic v1 + User.update_forward_refs(**locals()) # type: ignore[reportDeprecated] + + address = Address(street="123 Main St") + user = User(name="Diana", address=address) + data = {"user": user} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}' + + address_with_city = Address(street="456 Oak Ave", city="Boston") + user_verified = User(name="Eve", address=address_with_city, verified=True) + data = {"user": user_verified} + json_bytes = openapi_dumps(data) + assert ( + json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}' + ) + + def test_pydantic_model_with_optional_fields(self) -> None: + class User(pydantic.BaseModel): + name: str + email: Union[str, None] + phone: Union[str, None] + + model_with_none = User(name="Eve", email=None, phone=None) + data = {"model": model_with_none} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}' + + model_with_values = User(name="Frank", email="frank@example.com", phone=None) + data = {"model": model_with_values} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}'