FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[pull] main from Unstructured-IO:main by pull[bot] · Pull Request #3 · em3ndez/unstructured-python-client · GitHub

[pull] main from Unstructured-IO:main - #3

Open
pull[bot] wants to merge 166 commits into
em3ndez:mainfrom
Unstructured-IO:main
Open

[pull] main from Unstructured-IO:main#3
pull[bot] wants to merge 166 commits into
em3ndez:mainfrom
Unstructured-IO:main

Conversation

pull Bot commented Jun 5, 2024
edited
Loading

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot]

Can you help keep this open source service alive? 💖 Please sponsor : )

awalker4 and others added 4 commits June 4, 2024 17:27
We're now using asyncio for page split concurrency, but because the
client itself is not async, we need to manage our own event loop. This
complains if your environment already has a running event loop. For
instance, setting `split_pdf_page=True` in a jupyter cell will give you
`RuntimeError: This event loop is already running`.

Turns out there's a simple library to allow for nested event loops. We
just apply the monkeypatch in split_pdf_hook.py and the error goes away.

To verify, you'll need to run `pip install -e .` to install the local
version of the client. Run `make run-jupyter` and open up the sample
notebook in `_jupyter/`. Try making a request with page splitting
enabled and you'll see the above error. Then, check out this branch,
install locally again, restart your jupyter kernel, and the error is
fixed.
Changes:
* Bring the publish step back to the speakeasy workflow, regenerate the
github action with `speakeasy configure publishing`
* Remove incorrect readme note about parent_id being disabled
* Knock the package version back down to 0.23.0 for continuity with PyPI
versions
I added it to setup.py which of course is autogenerated.
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/816dffe1d4e68668beb03d2fbf94c6b2>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/d6609910d3434cf4bff19c21160c8da9>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.299.7 (2.338.12)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
pull Bot added the ⤵️ pull label Jun 5, 2024
awalker4 and others added 25 commits June 6, 2024 11:10
We're seeing an issue where the nest_asyncio.apply() workaround for
nested loops is breaking when we're dealing with a `uvloop`. We need to
investigate further, or remove the bandaid solution. In the meantime, we
can unblock a simple import of the client in these environments by doing
the apply only when the hook is run.
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/482056910a6dbf60d3f53a1dc5af27a5>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/37c93e98e1b0c8ec5531af6551eab9b0>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.300.0 (2.338.14)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Our docs page will be the source of truth, and the readme can have some
general information from the autogenerated sections.

Changes:
* Link to our docs at the top
* Remove the manually created usage snippet and swap out for the
autogenerated one. This way, the readme stays up to date while we focus
on the docs page.
* Bring back autogenerated error handling - this will populate the next
time the client regens
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/01db84f5f80c422ff8c0ad8355dd0e2d>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/09afe056dfd31b90a4ab59effebe7169>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.300.1 (2.339.1)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
In the PR introducing more logging two things turned out lacking:
- there was a false success log in split-page after error hook
- the retry logs were only for 5XX responses where speakeasy also has
retry set for ConnectionError exceptions

This PR removes the faulty success log, moves success/failure logs to the `LoggerHook` and introduces logging for `ConnectionError` retries.

---------

Co-authored-by: Filip Knefel <filip@unstructured.io>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/465e078af6f8af458707bb4481cdc70e>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/5e2e1bb90eaabe5859394ad003a71875>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.300.1 (2.339.1)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/c1fec31921ab085211de39f108a9ca23>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/a757f2f00046b6a6e109d6fcfb1abc22>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.308.1 (2.342.6)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary



```
├─┬Info
│ └──[🔀] version (1:80)
├─┬Paths
│ └─┬/general/v0/general
│   └─┬POST
│     └─┬Responses
│       ├──[➕] codes (1:1207)
│       └─┬200
│         └─┬application/json
│           └─┬Schema
│             └──[➕] description (1:957)
└─┬Components
  ├──[➕] schemas (1:10240)
  ├─┬partition_parameters
  │ ├─┬ocr_languages
  │ │ └──[🔀] description (1:4062)
  │ ├─┬xml_keep_tags
  │ │ └──[🔀] description (1:6020)
  │ ├─┬unique_element_ids
  │ │ └──[🔀] description (1:5728)
  │ ├─┬gz_uncompressed_content_type
  │ │ └──[🔀] description (1:3238)
  │ ├─┬include_page_breaks
  │ │ └──[🔀] description (1:3589)
  │ ├─┬coordinates
  │ │ └──[🔀] description (1:2526)
  │ ├─┬chunking_strategy
  │ │ └──[🔀] description (1:6380)
  │ ├─┬languages
  │ │ └──[🔀] description (1:3801)
  │ └─┬extract_image_block_types
  │   └──[🔀] description (1:2948)
  └─┬HTTPValidationError
    ├──[➖] title (1:1374)
    ├──[➕] example (1:1594)
    └─┬detail
      ├──[➖] items (1:1259)❌ 
      ├──[➖] type (1:1317)❌ 
      ├──[➖] title (1:1335)
      ├──[➕] oneOf (1:1482)
      └──[➕] oneOf (1:1560)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |
| paths            | 2             | 0                |
| components       | 17            | 2                |





## PYTHON CHANGELOG

## unions: 2.82.8 - 2024-06-10
### 🐛 Bug Fixes
- ensure union type definitions define types in a way compatible with
multiple python versions *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## core: 4.6.11 - 2024-06-14
### 👷 Build System
- fixed indentation as tabs in python makefile *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## core: 4.6.10 - 2024-06-13
### 👷 Build System
- move to new method of publishing for python *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*

---------

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Austin Walker <austin@unstructured.io>
Co-authored-by: Austin Walker <austin@unstructured.io>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/3aec785f32070a79daac48f3e257f88d>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/1ed855e6e969ec81ba5d8815b2cad456>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.308.1 (2.342.6)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
* Set the split_pdf_page default to true and run `make client-generate`
locally.
* Update the readme, add another reference back to our docs
* Change some warning logs to info. The user should not be warned about
default behavior for non pdf files

# Testing
Use the client locally and verify that split mode is the default, and
that the client behavior is consistent with older versions.

* Set up (or activate) your pyenv for the client: `pyenv virtualenv 3.12
unstructured-client; pyenv activate unstructured-client`
* Check out this branch and install: `pip install -e .`
* Run this sample script in the top level of the client repo. Try
different files in `_sample_docs` and verify that the logging and
results look acceptable.

```
from unstructured_client import UnstructuredClient
from unstructured_client.models import shared, operations

import json

api_key = "free-api-key"
filename = "_sample_docs/layout-parser-paper.pdf"

s = UnstructuredClient(
    api_key_auth=api_key,
)

with open(filename, "rb") as f:
    files=shared.Files(
        content=f.read(),
        file_name=filename,
    )

req = operations.PartitionRequest(
    shared.PartitionParameters(
        files=files,
        strategy=shared.Strategy.AUTO
    ),
)

try:
    resp = s.general.partition(req)
    print(json.dumps(resp.elements, indent=4))
except Exception as e:
    print(e)
```
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/807de4722a65543cde9dbb2bcba4e7cf>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/497ec57d30decc20c1a8ad59bc711aa3>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.308.1 (2.342.6)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary



```
└─┬Components
  └─┬partition_parameters
    └─┬split_pdf_page
      └──[🔀] default (1:9405)❌ 
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| components       | 1             | 1                |





## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Verified that this shows a speedup by doing a local pip install and
running the following snippet before and after the change:

```
from unstructured_client import UnstructuredClient
from unstructured_client.models import shared

s = UnstructuredClient(
    server_url=SERVER_URL,
    api_key_auth=API_KEY,
    )

filename = "../_sample_docs/layout-parser-paper.pdf"

with open(filename, "rb") as f:
    # Note that this currently only supports a single file
    files=shared.Files(
        content=f.read(),
        file_name=filename,
	)

req = shared.PartitionParameters(
    files=files,
    strategy="hi_res",
)

start_time = time.time()
resp = s.general.partition(req)
end_time = time.time()
print(f"Elapsed time: {end_time - start_time} seconds")
```
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/5969e484e45b07f1c14c78f621ee718a>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/f72de0f38cd597a806a71502c5287fb1>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.322.1 (2.354.2)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary



```
├─┬Info
│ └──[🔀] version (264:12)
├─┬Paths
│ └─┬/general/v0/general
│   └──POST
├──Servers
├──Servers
├─┬Components
│ ├─┬partition_parameters
│ │ ├──files
│ │ ├─┬output_format
│ │ │ ├──[➕] enum (177:15)
│ │ │ ├──[➕] enum (178:15)
│ │ │ ├──[➖] enum (1:4212)❌ 
│ │ │ └──[➖] enum (1:4232)❌ 
│ │ ├─┬chunking_strategy
│ │ │ └─┬ANYOF
│ │ │   ├──[➕] enum (68:19)
│ │ │   ├──[➕] enum (69:19)
│ │ │   ├──[➕] enum (70:19)
│ │ │   ├──[➕] enum (71:19)
│ │ │   ├──[➖] enum (1:6248)❌ 
│ │ │   ├──[➖] enum (1:6257)❌ 
│ │ │   ├──[➖] enum (1:6268)❌ 
│ │ │   └──[➖] enum (1:6285)❌ 
│ │ └─┬strategy
│ │   ├──[➕] enum (232:15)
│ │   ├──[➕] enum (233:15)
│ │   ├──[➕] enum (234:15)
│ │   ├──[➕] enum (235:15)
│ │   ├──[➖] enum (1:5403)❌ 
│ │   ├──[➖] enum (1:5377)❌ 
│ │   ├──[➖] enum (1:5385)❌ 
│ │   └──[➖] enum (1:5395)❌ 
│ ├─┬Element
│ │ └──[🔀] example (6:9)
│ ├─┬ServerError
│ │ └──[🔀] example (36:9)
│ ├─┬HTTPValidationError
│ │ └──[🔀] example (19:9)
│ └──ApiKeyAuth
└─┬Extensions
  └──[🔀] x-speakeasy-retries (325:3)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| paths            | 0             | 0                |
| servers          | 0             | 0                |
| components       | 23            | 10               |
| info             | 1             | 0                |





## PYTHON CHANGELOG

## core: 4.6.13 - 2024-06-21
### 🔧 Chores
- update contribution section wording *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 0.2.4 - 2024-06-21
### 🔧 Chores
- update contribution section wording *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 4.6.12 - 2024-06-20
### 🐛 Bug Fixes
- test response status codes in sdk methods in order of specificity
*(commit by [@disintegrator](https://github.com/disintegrator))*



## core: 0.2.3 - 2024-06-20
### 🐛 Bug Fixes
- test response status codes in sdk methods in order of specificity
*(commit by [@disintegrator](https://github.com/disintegrator))*



## core: 0.2.2 - 2024-06-19
### 🐛 Bug Fixes
- various fixes for field naming, typedict serialization and tests
*(commit by [@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## core: 0.2.1 - 2024-06-19
### 🐛 Bug Fixes
- generation of reserved field names *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## core: 0.2.0 - 2024-06-18
### 🐝 New Features
- added support for structural typing *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Austin Walker <awalk89@gmail.com>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/4f0ce155b8184add31556e4e9df60a84>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/25efa7d832228704d26082e3827858f9>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.327.0 (2.359.6)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary



```
├─┬Paths
│ └─┬/general/v0/general
│   └──POST
├──Servers
├──Servers
├─┬Components
│ ├─┬Element
│ │ └──[🔀] example (283:22)
│ ├─┬HTTPValidationError
│ │ └──[🔀] example (65:22)
│ ├─┬ServerError
│ │ └──[🔀] example (289:22)
│ ├─┬partition_parameters
│ │ ├─┬output_format
│ │ │ ├──[➕] enum (150:27)
│ │ │ ├──[➕] enum (151:27)
│ │ │ ├──[➖] enum (177:27)❌ 
│ │ │ └──[➖] enum (178:27)❌ 
│ │ ├──files
│ │ ├─┬chunking_strategy
│ │ │ └─┬ANYOF
│ │ │   ├──[➕] enum (203:31)
│ │ │   ├──[➕] enum (204:31)
│ │ │   ├──[➕] enum (205:31)
│ │ │   ├──[➕] enum (206:31)
│ │ │   ├──[➖] enum (68:31)❌ 
│ │ │   ├──[➖] enum (69:31)❌ 
│ │ │   ├──[➖] enum (70:31)❌ 
│ │ │   └──[➖] enum (71:31)❌ 
│ │ └─┬strategy
│ │   ├──[➕] enum (179:27)
│ │   ├──[➕] enum (180:27)
│ │   ├──[➕] enum (181:27)
│ │   ├──[➕] enum (178:27)
│ │   ├──[➖] enum (232:27)❌ 
│ │   ├──[➖] enum (233:27)❌ 
│ │   ├──[➖] enum (234:27)❌ 
│ │   └──[➖] enum (235:27)❌ 
│ └──ApiKeyAuth
└─┬Extensions
  └──[🔀] x-speakeasy-retries (300:22)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| paths            | 0             | 0                |
| servers          | 0             | 0                |
| components       | 23            | 10               |





## PYTHON CHANGELOG

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/86d52c444980b204248909ddaee6938f>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/507dd83771f282dbf344b92b10df023f>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.330.0 (2.361.10)
https://github.com/speakeasy-api/speakeasy



## PYTHON CHANGELOG

## core: 4.8.0 - 2024-07-05
### 🐝 New Features
- add timeout config to pythonv2 operations and sdk *(commit by
[@ryan-timothy-albert](https://github.com/ryan-timothy-albert))*



## core: 0.2.9 - 2024-07-04
### 🔧 Chores
- reduce response matching boilerplate *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 0.2.8 - 2024-07-02
### 🐛 Bug Fixes
- use None as arg default instead of UNSET *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 4.7.0 - 2024-06-27
### 🐝 New Features
- add env variable global security support *(commit by
[@ryan-timothy-albert](https://github.com/ryan-timothy-albert))*



## core: 4.6.14 - 2024-06-27
### 🐛 Bug Fixes
- remove unnecessary accept_header_override documentation elements
*(commit by [@ThomasRooney](https://github.com/ThomasRooney))*



## core: 0.2.6 - 2024-06-27
### 🐛 Bug Fixes
- add "input" to reserved keywords in pythonv2 *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 0.2.5 - 2024-06-26
### 🐛 Bug Fixes
- disallow positional arguments in python v2 SDKs *(commit by
[@disintegrator](https://github.com/disintegrator))*

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
# New parameter
Add a client side param called `split_pdf_page_range` which takes a list
of two integers, `[start_page, end_page]`. If `split_pdf_page` is `True`
and a range is set, slice the doc from `start_page` up to and including
`end_page`. Only this page range will be sent to the API. The subset of
pages is still split up as needed.

# Other changes
Allow our custom hooks to properly access list parameters, so we're able
to intercept `split_pdf_page_range`. We need extra handling to get list
params out of the request in `parse_form_data`, and to rebuild the
payload in `create_request_body`.

# Testing
Check out this branch and set up a request to your local API:

```
    client = UnstructuredClient(api_key_auth="", server_url="localhost:8000")

    filename = "_sample_docs/layout-parser-paper.pdf"
    with open(filename, "rb") as f:
        files = shared.Files(
            content=f.read(),
            file_name=filename,
        )

    req = shared.PartitionParameters(
        files=files,
        strategy="fast",
        split_pdf_page=True,
        split_pdf_page_range=[1, 16],
    )

    resp = client.general.partition(req)

```

Test out various page ranges and confirm that the returned elements are
within the range. Invalid ranges should throw a ValueError (pages are
out of bounds, or end_page < start_page).
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.335.0 (2.370.2)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary


```
└─┬Components
  └─┬partition_parameters
    └──[➕] properties (270:17)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| components       | 1             | 0                |



## PYTHON CHANGELOG
## globalSecurity: 2.83.5 - 2024-03-15
### 🐛 Bug Fixes
- fixed hoisting of operation security *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## openEnums: 0.1.0 - 2024-05-14
### 🐝 New Features
- add support for "open" enums *(commit by
[@disintegrator](https://github.com/disintegrator))*



## responseFormat: 0.1.0 - 2024-03-02
### 🐝 New Features
- add support for response formats and flat responses *(commit by
[@TristanSpeakEasy](https://github.com/TristanSpeakeasy))*



## examples: 2.81.3 - 2023-10-17
### 🔧 Chores
- remove multi word generated examples *(commit by
[@ThomasRooney](https://github.com/ThomasRooney))*



## nameOverrides: 2.81.2 - 2024-03-25
### 🐛 Bug Fixes
- x-speakeasy-name-overrides being missed when used under an allOf
*(commit by [@ThomasRooney](https://github.com/ThomasRooney))*



## retries: 2.82.2 - 2024-04-10
### 🐛 Bug Fixes
- add method to correctly case retryConnectionErrors *bool for Python
generation *(commit by [@AshGodfrey](https://github.com/AshGodfrey))*



## constsAndDefaults: 0.1.3 - 2024-03-01
### 🐛 Bug Fixes
- null enums are coerced into null consts *(commit by
[@disintegrator](https://github.com/disintegrator))*



## core: 4.8.1 - 2024-07-09
### 🐛 Bug Fixes
- Use 0666 file mode for writing configuration and lock files *(commit
by [@bflad](https://github.com/bflad))*



## unions: 2.82.8 - 2024-06-10
### 🐛 Bug Fixes
- ensure union type definitions define types in a way compatible with
multiple python versions *(commit by
[@TristanSpeakEasy](https://github.com/tristanspeakeasy))*



## globalServerURLs: 2.82.2 - 2024-03-06
### 🔧 Chores
- expand server selection test coverage *(commit by
[@2ynn](https://github.com/2ynn))*

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
The autogenerated example is inconsistent with our hosted docs. Remove
this section and copy the usage snippet from the docs. Also, add a note
for the new page range feature.
For easier testing of local API changes. Run the server at port 5000,
and then use `make client-generate-local` to see how the SDK changes.
The default server url is changing to serverless. Therefore, if you get
a 401, we should suggest that you meant to use the free api. Also, bump
the minor version in anticipation of the url change.
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/4ef3c768cbc0fbb7b563d54428c392bd>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/16a148a70bdf953ba9878e409aaf5b40>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.348.1 (2.380.1)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary


```
├─┬Info
│ └──[🔀] version (4:14)
└─┬Servers
  └──[➕] servers (6:7)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |
| servers          | 1             | 0                |

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
This PR:
- adds `split_pdf_page_allow_failed` parameter
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/71b44e40cb066dc55aa5e45b3e48d59d>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/e55599ebf9e42a00dff8584ddcb9e210>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.349.0 (2.382.0)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary


```
└─┬Components
  └─┬partition_parameters
    ├──[➕] properties (287:17)
    ├─┬hi_res_model_name
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (128:33)❌ 
    │ │ └──[➖] default (127:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (126:33)❌ 
    ├─┬similarity_threshold
    │ └─┬ANYOF
    │   └──[➖] default (264:36)❌ 
    ├─┬starting_page_number
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (175:33)❌ 
    │ │ └──[➖] default (174:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (173:33)❌ 
    ├─┬combine_under_n_chars
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (222:33)❌ 
    │ │ └──[➖] default (221:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (220:33)❌ 
    ├─┬gz_uncompressed_content_type
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (121:33)❌ 
    │ │ └──[➖] default (120:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (119:33)❌ 
    ├─┬include_orig_elements
    │ ├─┬ANYOF
    │ │ └──[🔀] type (227:33)❌ 
    │ └─┬ANYOF
    │   ├──[🔀] type (229:33)❌ 
    │   └──[➖] default (228:36)❌ 
    ├─┬max_characters
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (236:33)❌ 
    │ │ └──[➖] default (235:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (234:33)❌ 
    ├─┬new_after_n_chars
    │ ├─┬ANYOF
    │ │ ├──[🔀] type (248:33)❌ 
    │ │ └──[➖] default (247:36)❌ 
    │ └─┬ANYOF
    │   └──[🔀] type (246:33)❌ 
    ├─┬chunking_strategy
    │ ├─┬ANYOF
    │ │ ├──[➕] enum (206:31)
    │ │ ├──[➕] enum (207:31)
    │ │ ├──[➕] enum (208:31)
    │ │ ├──[➕] enum (209:31)
    │ │ └──[🔀] type (204:33)❌ 
    │ └─┬ANYOF
    │   ├──[➖] enum (206:31)❌ 
    │   ├──[➖] enum (207:31)❌ 
    │   ├──[➖] enum (208:31)❌ 
    │   ├──[➖] enum (209:31)❌ 
    │   ├──[🔀] type (211:33)❌ 
    │   └──[➖] default (210:36)❌ 
    └─┬encoding
      ├─┬ANYOF
      │ ├──[🔀] type (107:33)❌ 
      │ └──[➖] default (106:36)❌ 
      └─┬ANYOF
        └──[🔀] type (105:33)❌ 
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| components       | 37            | 32               |

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
PDF page splitting uses asyncio but the SDK is not async. Therefore, we
had to manage our own event loop, which can lead to issues in other
event loop contexts. Uvloop is one context that does not allow us to use
nested event loops. When we find ourselves in a uvloop.Loop, we have to
fallback to non splitting mode. #135 will make the whole SDK async so we
don't have to hack this.

Closes #133
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/linting-report/38839b02490ec839f218de5cb3d8322b>
> OpenAPI Change report available at:
<https://app.speakeasyapi.dev/org/unstructured/unstructured5xr/changes-report/ce3f859bd05781d66a86db427a72746f>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.352.1 (2.385.1)
https://github.com/speakeasy-api/speakeasy
## OpenAPI Change Summary


```
└─┬Info
  └──[🔀] version (4:14)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Austin Walker <awalk89@gmail.com>
awalker4 and others added 30 commits August 6, 2025 15:33
Use the openapi overlay file to add `x-speakeasy-unknown-values` to the
models for `SourceConnectorType` and `DestinationConnectorType`. This
allows the client to send any string outside of the enum definitions.
This provides forward compatibility in the client, we can create sources
with types that don't exist yet, without requiring a new client version.

Example:

This throws an error that the type is not in our enum. Now, it just
warns that this is an unknown value, but it's sent to the server anyway.

```
res = unstructured_client.sources.create_source(
    request={
        "create_source_connector": {
            "name": "My fancy new source",
            "type": "future_source_type",
            "config": {
                ...
                ...
            }
        }
    }
)
```

The change will take effect when the client regenerates and uses the new
overlay config. By setting
`gen.yaml` to `0.42.2`, the new client will propagate this version
change.
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/0a5f5e6fa72220658eebd2029258b3f1>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/ab67ee934e789814b24e905291d94612>
# SDK update
Based on:
- OpenAPI Doc  
- Speakeasy CLI 1.598.3 (2.674.3)
https://github.com/speakeasy-api/speakeasy
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
## PYTHON CHANGELOG
## core: 5.19.7 - 2025-08-06
### 🐛 Bug Fixes
- add return type hint for methods returning None *(commit by
[@AshGodfrey](https://github.com/AshGodfrey))*

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/7a385740ded90a1b1df8b2685f4e1181>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/eaa1b1b1959d9dbe3784727572638377>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
<details>
<summary>OpenAPI Change Summary</summary>


```
└─┬Components
  ├─┬CreateDestinationConnector
  │ └─┬config
  │   └──[➕] anyOf (2768:15)
  ├─┬DestinationConnectorInformation
  │ └─┬config
  │   └──[➕] anyOf (3428:15)
  ├─┬UpdateSourceConnector
  │ └─┬config
  │   └──[➕] anyOf (6675:15)
  ├─┬UpdateDestinationConnector
  │ └─┬config
  │   └──[➕] anyOf (6597:15)
  ├─┬SourceConnectorInformation
  │ └─┬config
  │   └──[➕] anyOf (6467:15)
  └─┬CreateSourceConnector
    └─┬config
      └──[➕] anyOf (2855:15)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| components       | 6             | 0                |


</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

---------

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Austin Walker <austin@unstructured.io>
- This is done by updating the version constraint in `gen.yaml` and then
running `make client-generate-sdk`
- I've removed other new changes in order to keep this pr small
- By manually touching `RELEASES.md` we'll trigger a package release in
this repo
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/2db2473a26de998125d5cf53ccfaf5e1>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/1b820d9c7c5a64aedcba48f48cbf1b8b>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
## Python SDK Changes:
* `unstructured_client.workflows.get_workflow()`:
`response.reprocess_all` **Changed** **Breaking** ⚠️
* `unstructured_client.sources.create_source()`: 
* `request.create_source_connector.config` **Changed** **Breaking**
⚠️
* `response.config.[snowflake_source_connector_config].schema`
**Changed**
* `unstructured_client.general.partition()`: 
  *  `request.partition_parameters` **Changed** **Breaking** ⚠️
* `unstructured_client.destinations.create_destination()`: 
* `request.create_destination_connector.config` **Changed** **Breaking**
⚠️
  *  `response.config` **Changed**
* `unstructured_client.workflows.update_workflow()`: 
  *  `request.update_workflow.template_id` **Added**
  *  `response.reprocess_all` **Changed** **Breaking** ⚠️
* `unstructured_client.workflows.list_workflows()`:
`response.[].reprocess_all` **Changed** **Breaking** ⚠️
* `unstructured_client.destinations.update_destination()`: 
* `request.update_destination_connector.config` **Changed** **Breaking**
⚠️
  *  `response.config` **Changed**
* `unstructured_client.workflows.create_workflow()`: 
  *  `request.create_workflow.template_id` **Added**
  *  `response.reprocess_all` **Changed** **Breaking** ⚠️
* `unstructured_client.sources.update_source()`: 
* `request.update_source_connector.config` **Changed** **Breaking**
⚠️
* `response.config.[snowflake_source_connector_config].schema`
**Changed**
* `unstructured_client.destinations.list_destinations()`: 
  *  `request.destination_type` **Changed**
  *  `response.[].config` **Changed**
* `unstructured_client.sources.list_sources()`:
`response.[].config.[snowflake_source_connector_config].schema`
**Changed**
* `unstructured_client.sources.get_source()`:
`response.config.[snowflake_source_connector_config].schema` **Changed**
* `unstructured_client.templates.get_template()`: **Added**
* `unstructured_client.jobs.list_jobs()`:
`response.[].output_node_files.[]` **Changed**
* `unstructured_client.jobs.download_job_output()`: 
  *  `request.node_id` **Changed**
* `unstructured_client.jobs.create_job()`: **Added**
* `unstructured_client.jobs.get_job()`: `response.output_node_files.[]`
**Changed**
* `unstructured_client.workflows.run_workflow()`:
`response.output_node_files.[]` **Changed**
* `unstructured_client.destinations.get_destination()`:
`response.config` **Changed**
* `unstructured_client.templates.list_templates()`: **Added**

<details>
<summary>OpenAPI Change Summary</summary>


```
├─┬Info
│ └──[🔀] version (18:16)
├─┬Paths
│ ├──[➕] path (1356:5)
│ ├──[➕] path (1410:5)
│ ├─┬/api/v1/jobs/{job_id}/download
│ │ └─┬GET
│ │   └─┬Parameters
│ │     ├──[🔀] description (1181:28)
│ │     ├──[🔀] required (1182:25)❌ 
│ │     └─┬Schema
│ │       ├──[➖] type (1:15945)❌ 
│ │       ├──[➖] format (1:15985)❌ 
│ │       ├──[🔀] description (1194:30)
│ │       ├──[➕] anyOf (1185:17)
│ │       └──[➕] anyOf (1189:17)
│ └─┬/api/v1/jobs/
│   └──[➕] post (971:15)
└─┬Components
  ├──[➕] schemas (2511:26)
  ├──[➕] schemas (2271:42)
  ├──[➕] schemas (2328:47)
  ├──[➕] schemas (6970:23)
  ├──[➕] schemas (6936:27)
  ├──[➕] schemas (6894:25)
  ├─┬partition_parameters
  │ ├──[➕] properties (7981:11)
  │ ├─┬table_ocr_agent
  │ │ ├──[➕] examples (7808:15)
  │ │ ├──[➕] examples (7809:15)
  │ │ ├──[➕] examples (7810:15)
  │ │ ├──[➕] enum (7814:15)
  │ │ ├──[➕] enum (7815:15)
  │ │ ├──[➕] enum (7816:15)
  │ │ ├──[➕] enum (7817:15)
  │ │ ├──[➕] enum (7818:15)
  │ │ ├──[➕] enum (7819:15)
  │ │ ├──[➕] type (7806:21)❌ 
  │ │ ├──[🔀] title (7812:22)
  │ │ ├──[🔀] description (7821:28)
  │ │ ├──[🔀] default (7822:24)❌ 
  │ │ ├──[➖] anyOf (1:99430)❌ 
  │ │ ├──[➖] anyOf (1:99467)❌ 
  │ │ └─┬Extensions
  │ │   └──[➕] x-speakeasy-unknown-values (7823:43)
  │ ├─┬hi_res_model_name
  │ │ ├──[🔀] title (7726:22)
  │ │ └──[🔀] description (7727:28)
  │ ├─┬multipage_sections
  │ │ ├──[➖] type (1:101553)❌ 
  │ │ ├──[🔀] title (7934:22)
  │ │ ├──[🔀] description (7935:28)
  │ │ ├──[➖] default (1:101718)❌ 
  │ │ ├──[➕] anyOf (7926:15)
  │ │ └──[➕] anyOf (7930:15)
  │ ├─┬split_pdf_page_range
  │ │ ├──[➖] items (1:105310)❌ 
  │ │ ├──[🔀] type (8047:21)❌ 
  │ │ ├──[🔀] title (8048:22)
  │ │ ├──[➖] maxItems (1:105358)❌ 
  │ │ ├──[➖] minItems (1:105343)❌ 
  │ │ ├──[🔀] description (8049:28)
  │ │ ├──[➕] default (8050:24)❌ 
  │ │ └──[➖] example (1:105372)
  │ ├─┬gz_uncompressed_content_type
  │ │ ├──[➕] examples (7667:15)
  │ │ ├──[➕] type (7665:21)❌ 
  │ │ ├──[➖] title (1:95995)
  │ │ ├──[➕] format (7672:23)❌ 
  │ │ ├──[🔀] description (7673:28)
  │ │ ├──[➖] anyOf (1:95930)❌ 
  │ │ └──[➖] anyOf (1:95967)❌ 
  │ ├─┬new_after_n_chars
  │ │ ├──[➕] type (7938:21)❌ 
  │ │ ├──[🔀] title (7939:22)
  │ │ ├──[🔀] description (7940:28)
  │ │ ├──[➕] default (7941:24)❌ 
  │ │ ├──[➖] anyOf (1:101757)❌ 
  │ │ └──[➖] anyOf (1:101795)❌ 
  │ ├─┬split_pdf_cache_tmp_data
  │ │ ├──[🔀] title (8073:22)
  │ │ └──[🔀] description (8074:28)
  │ ├─┬vlm_model
  │ │ ├──[➖] enum (1:99128)❌ 
  │ │ ├──[➖] enum (1:99173)❌ 
  │ │ ├──[➖] enum (1:99257)❌ 
  │ │ ├──[➖] enum (1:99295)❌ 
  │ │ ├──[➖] enum (1:98875)❌ 
  │ │ ├──[➖] enum (1:98905)❌ 
  │ │ ├──[➖] enum (1:98988)❌ 
  │ │ ├──[➖] enum (1:99036)❌ 
  │ │ ├──[➖] enum (1:99084)❌ 
  │ │ ├──[➖] enum (1:99219)❌ 
  │ │ ├──[➖] enum (1:98845)❌ 
  │ │ ├──[➖] enum (1:98915)❌ 
  │ │ ├──[➖] enum (1:98933)❌ 
  │ │ └──[➖] enum (1:98960)❌ 
  │ ├─┬pdfminer_char_margin
  │ │ ├──[➕] type (7778:21)❌ 
  │ │ ├──[🔀] title (7779:22)
  │ │ ├──[🔀] description (7780:28)
  │ │ ├──[➕] default (7781:24)❌ 
  │ │ ├──[➖] anyOf (1:103676)❌ 
  │ │ └──[➖] anyOf (1:103713)❌ 
  │ ├─┬vlm_model_provider
  │ │ ├──[➕] examples (7829:15)
  │ │ └──[➕] examples (7830:15)
  │ ├─┬split_pdf_concurrency_level
  │ │ ├──[🔀] type (8087:21)❌ 
  │ │ ├──[🔀] title (8085:22)
  │ │ ├──[🔀] description (8086:28)
  │ │ └──[🔀] default (8088:24)❌ 
  │ ├─┬strategy
  │ │ ├──[➖] examples (1:98182)
  │ │ ├──[➖] examples (1:98190)
  │ │ ├──[➖] examples (1:98200)
  │ │ ├──[➖] enum (1:98246)❌ 
  │ │ ├──[➖] enum (1:98256)❌ 
  │ │ ├──[➖] enum (1:98264)❌ 
  │ │ ├──[➖] enum (1:98276)❌ 
  │ │ ├──[➖] enum (1:98287)❌ 
  │ │ ├──[➖] enum (1:98238)❌ 
  │ │ ├──[➖] type (1:98159)❌ 
  │ │ ├──[🔀] title (7802:22)
  │ │ ├──[🔀] description (7803:28)
  │ │ ├──[➖] default (1:98420)❌ 
  │ │ ├──[➕] anyOf (7794:15)
  │ │ ├──[➕] anyOf (7798:15)
  │ │ └─┬Extensions
  │ │   └──[➖] x-speakeasy-unknown-values (1:98460)❌ 
  │ ├─┬overlap_all
  │ │ ├──[🔀] type (7957:21)❌ 
  │ │ ├──[🔀] title (7958:22)
  │ │ ├──[🔀] description (7959:28)
  │ │ └──[🔀] default (7960:24)❌ 
  │ ├─┬pdfminer_line_overlap
  │ │ ├──[🔀] title (8029:22)
  │ │ └──[🔀] description (8030:28)
  │ ├─┬pdfminer_line_margin
  │ │ ├──[🔀] title (8016:22)
  │ │ └──[🔀] description (8017:28)
  │ ├─┬ocr_languages
  │ │ ├──[➖] items (1:96802)❌ 
  │ │ ├──[➖] type (1:96784)❌ 
  │ │ ├──[🔀] title (7953:22)
  │ │ ├──[🔀] description (7954:28)
  │ │ ├──[➖] default (1:96963)❌ 
  │ │ ├──[➕] anyOf (7945:15)
  │ │ └──[➕] anyOf (7949:15)
  │ ├─┬max_characters
  │ │ ├──[➕] items (7750:22)❌ 
  │ │ ├──[➕] type (7749:21)❌ 
  │ │ ├──[🔀] title (7753:22)
  │ │ ├──[🔀] description (7754:28)
  │ │ ├──[➕] default (7755:24)❌ 
  │ │ ├──[➖] anyOf (1:101309)❌ 
  │ │ └──[➖] anyOf (1:101347)❌ 
  │ ├─┬include_page_breaks
  │ │ ├──[➖] type (1:96332)❌ 
  │ │ ├──[🔀] title (7921:22)
  │ │ ├──[🔀] description (7922:28)
  │ │ ├──[➖] default (1:96493)❌ 
  │ │ ├──[➕] anyOf (7913:15)
  │ │ └──[➕] anyOf (7917:15)
  │ ├─┬split_pdf_page
  │ │ ├──[🔀] type (8069:21)❌ 
  │ │ ├──[🔀] title (8067:22)
  │ │ ├──[🔀] description (8068:28)
  │ │ └──[🔀] default (8070:24)❌ 
  │ ├─┬overlap
  │ │ ├──[➕] enum (7770:15)
  │ │ ├──[➕] enum (7771:15)
  │ │ ├──[🔀] type (7767:21)❌ 
  │ │ ├──[🔀] title (7768:22)
  │ │ ├──[🔀] description (7773:28)
  │ │ ├──[🔀] default (7774:24)❌ 
  │ │ └─┬Extensions
  │ │   └──[➕] x-speakeasy-unknown-values (7775:43)
  │ ├─┬languages
  │ │ ├──[➖] items (1:96541)❌ 
  │ │ ├──[🔀] type (7988:21)❌ 
  │ │ ├──[🔀] title (7989:22)
  │ │ ├──[🔀] description (7990:28)
  │ │ └──[🔀] default (7991:24)❌ 
  │ ├─┬skip_infer_table_types
  │ │ ├──[➖] items (1:97662)❌ 
  │ │ ├──[➖] type (1:97644)❌ 
  │ │ ├──[🔀] title (7978:22)
  │ │ ├──[🔀] description (7979:28)
  │ │ ├──[➖] default (1:97822)❌ 
  │ │ ├──[➕] anyOf (7970:15)
  │ │ └──[➕] anyOf (7974:15)
  │ ├─┬include_slide_notes
  │ │ ├──[🔀] title (7744:22)
  │ │ ├──[🔀] description (7745:28)
  │ │ └──[🔀] default (7746:24)❌ 
  │ ├─┬pdfminer_word_margin
  │ │ ├──[🔀] title (8003:22)
  │ │ ├──[🔀] description (8004:28)
  │ │ └──[➖] default (1:104628)❌ 
  │ ├─┬split_pdf_allow_failed
  │ │ ├──[➕] items (7785:22)❌ 
  │ │ ├──[🔀] type (7784:21)❌ 
  │ │ ├──[🔀] title (7788:22)
  │ │ ├──[🔀] description (7789:28)
  │ │ └──[🔀] default (7790:24)❌ 
  │ ├─┬include_orig_elements
  │ │ ├──[🔀] title (7739:22)
  │ │ ├──[🔀] description (7740:28)
  │ │ └─┬ANYOF
  │ │   └──[🔀] type (7732:25)❌ 
  │ ├─┬extract_image_block_types
  │ │ ├──[➖] items (1:95673)❌ 
  │ │ ├──[➖] type (1:95655)❌ 
  │ │ ├──[🔀] title (7704:22)
  │ │ ├──[🔀] description (7705:28)
  │ │ ├──[➖] default (1:95882)❌ 
  │ │ ├──[➕] anyOf (7696:15)
  │ │ └──[➕] anyOf (7700:15)
  │ ├─┬pdf_infer_table_structure
  │ │ ├──[🔀] title (7964:22)
  │ │ ├──[🔀] description (7965:28)
  │ │ └──[🔀] default (7966:24)❌ 
  │ ├─┬similarity_threshold
  │ │ ├──[🔀] title (8042:22)
  │ │ ├──[🔀] description (8043:28)
  │ │ └──[➕] default (8044:24)❌ 
  │ ├─┬split_pdf_cache_tmp_data_dir
  │ │ ├──[🔀] type (8081:21)❌ 
  │ │ ├──[🔀] title (8079:22)
  │ │ ├──[🔀] description (8080:28)
  │ │ └──[🔀] default (8082:24)❌ 
  │ ├─┬unique_element_ids
  │ │ ├──[➖] type (1:99696)❌ 
  │ │ ├──[🔀] title (7864:22)
  │ │ ├──[🔀] description (7865:28)
  │ │ ├──[🔀] default (7866:24)❌ 
  │ │ ├──[➕] anyOf (7856:15)
  │ │ └──[➕] anyOf (7860:15)
  │ ├─┬output_format
  │ │ ├──[➖] enum (1:97039)❌ 
  │ │ ├──[➖] enum (1:97059)❌ 
  │ │ ├──[➕] items (7759:22)❌ 
  │ │ ├──[🔀] type (7758:21)❌ 
  │ │ ├──[🔀] title (7762:22)
  │ │ ├──[🔀] description (7763:28)
  │ │ ├──[🔀] default (7764:24)❌ 
  │ │ └─┬Extensions
  │ │   └──[➖] x-speakeasy-unknown-values (1:97259)❌ 
  │ ├─┬files
  │ │ ├──[➖] examples (1:94708)
  │ │ ├──[➕] items (7709:22)❌ 
  │ │ ├──[🔀] type (7708:21)❌ 
  │ │ ├──[➕] title (7712:22)
  │ │ ├──[➖] format (1:94909)❌ 
  │ │ ├──[🔀] description (7713:28)
  │ │ └──[➕] default (7714:24)❌ 
  │ ├─┬starting_page_number
  │ │ ├──[➕] items (8056:22)❌ 
  │ │ ├──[➕] type (8053:21)❌ 
  │ │ ├──[🔀] title (8054:22)
  │ │ ├──[➕] maxItems (8060:25)❌ 
  │ │ ├──[➕] minItems (8059:25)❌ 
  │ │ ├──[🔀] description (8055:28)
  │ │ ├──[➕] example (8061:24)
  │ │ ├──[➖] anyOf (1:97862)❌ 
  │ │ └──[➖] anyOf (1:97900)❌ 
  │ ├─┬encoding
  │ │ ├──[➕] type (7982:21)❌ 
  │ │ ├──[🔀] title (7983:22)
  │ │ ├──[🔀] description (7984:28)
  │ │ ├──[➕] default (7985:24)❌ 
  │ │ ├──[➖] anyOf (1:95455)❌ 
  │ │ └──[➖] anyOf (1:95492)❌ 
  │ └─┬xml_keep_tags
  │   ├──[➕] examples (7828:15)
  │   ├──[➕] examples (7829:15)
  │   ├──[➕] examples (7830:15)
  │   ├──[➕] enum (7839:15)
  │   ├──[➕] enum (7840:15)
  │   ├──[➕] enum (7834:15)
  │   ├──[➕] enum (7835:15)
  │   ├──[➕] enum (7836:15)
  │   ├──[➕] enum (7837:15)
  │   ├──[➕] enum (7838:15)
  │   ├──[🔀] type (7826:21)❌ 
  │   ├──[🔀] title (7832:22)
  │   ├──[🔀] description (7842:28)
  │   ├──[➖] default (1:100205)❌ 
  │   └─┬Extensions
  │     └──[➕] x-speakeasy-unknown-values (7843:43)
  ├─┬DestinationConnectorInformation
  │ └─┬config
  │   ├──[➕] anyOf (3778:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2272:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2129:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2225:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2795:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3553:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3367:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3639:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3890:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3968:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4770:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4970:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5105:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5152:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5248:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5539:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5596:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5863:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5806:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5998:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6446:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7318:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4171:17)❌ 
  ├─┬AstraDBConnectorConfigInput
  │ ├──[➕] properties (2198:11)
  │ ├─┬token
  │ │ ├──[➖] type (1:27451)❌ 
  │ │ ├──[🔀] title (2189:22)
  │ │ ├──[➕] default (2190:24)❌ 
  │ │ ├──[➕] anyOf (2182:15)
  │ │ └──[➕] anyOf (2185:15)
  │ ├─┬collection_name
  │ │ ├──[🔀] type (2199:21)❌ 
  │ │ ├──[🔀] title (2200:22)
  │ │ ├──[➖] pattern (1:27159)❌ 
  │ │ └──[➕] default (2201:24)❌ 
  │ ├─┬keyspace
  │ │ ├──[➕] type (2212:21)❌ 
  │ │ ├──[🔀] title (2213:22)
  │ │ ├──[🔀] default (2214:24)❌ 
  │ │ ├──[➖] anyOf (1:27193)❌ 
  │ │ └──[➖] anyOf (1:27213)❌ 
  │ └─┬flatten_metadata
  │   ├──[🔀] type (2176:21)❌ 
  │   ├──[🔀] title (2177:22)
  │   ├──[➕] pattern (2178:24)❌ 
  │   └──[➖] default (1:27560)❌ 
  ├─┬WorkflowInformation
  │ └─┬reprocess_all
  │   ├──[➕] type (7451:21)❌ 
  │   ├──[➕] default (7453:24)❌ 
  │   ├──[➖] anyOf (1:92249)❌ 
  │   └──[➖] anyOf (1:92270)❌ 
  ├─┬DatabricksVolumesConnectorConfigInput
  │ └─┬schema
  │   └──[➕] default (3609:24)❌ 
  ├─┬UpdateWorkflow
  │ ├──[➕] properties (7235:11)
  │ ├─┬workflow_type
  │ │ ├──[➕] title (7233:22)
  │ │ └─┬ANYOF
  │ │   └──[🔀] $ref (7223:15)❌ 
  │ └─┬workflow_nodes
  │   ├──[🔀] title (7244:22)
  │   └─┬ANYOF
  │     ├──[➖] items (1:89762)❌ 
  │     └──[🔀] type (7238:25)❌ 
  ├─┬SnowflakeDestinationConnectorConfig
  │ └──[➕] required (6514:11)❌ 
  ├─┬SnowflakeSourceConnectorConfig
  │ └──[➕] required (6663:11)❌ 
  ├─┬NodeFileMetadata
  │ ├──[➕] required (5243:11)❌ 
  │ ├──[➕] required (5244:11)❌ 
  │ ├──[➕] properties (5234:11)
  │ └──[➕] properties (5230:11)
  ├─┬SnowflakeDestinationConnectorConfigInput
  │ └──[➕] required (6586:11)❌ 
  ├─┬CreateDestinationConnector
  │ └─┬config
  │   ├──[➕] anyOf (3102:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2329:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2173:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2248:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2850:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3596:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3460:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3667:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3916:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3986:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4819:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5037:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5128:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5185:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5286:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5567:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5639:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5929:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5834:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6060:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6518:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7347:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4239:17)❌ 
  ├─┬SnowflakeSourceConnectorConfigInput
  │ └──[➕] required (6742:11)❌ 
  ├─┬CreateWorkflow
  │ ├──[➕] properties (3252:11)
  │ ├─┬workflow_type
  │ │ └──[🔀] $ref (3238:29)❌ 
  │ └─┬workflow_nodes
  │   ├──[🔀] title (3261:22)
  │   └─┬ANYOF
  │     ├──[➖] items (1:39512)❌ 
  │     └──[🔀] type (3255:25)❌ 
  ├─┬UpdateDestinationConnector
  │ └─┬config
  │   ├──[➕] anyOf (7082:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2329:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2173:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2248:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (2850:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3596:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3460:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3667:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3916:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (3986:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4819:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5037:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5128:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5185:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5286:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5567:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5639:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5929:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5834:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6060:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6518:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7347:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4239:17)❌ 
  ├─┬DatabricksVDTDestinationConnectorConfigInput
  │ └─┬schema
  │   └──[➕] default (3526:24)❌ 
  ├─┬AstraDBConnectorConfig
  │ ├──[➕] properties (2150:11)
  │ ├─┬token
  │ │ ├──[➖] type (1:26879)❌ 
  │ │ ├──[🔀] title (2144:22)
  │ │ ├──[➕] anyOf (2137:15)
  │ │ └──[➕] anyOf (2140:15)
  │ ├─┬collection_name
  │ │ ├──[🔀] type (2151:21)❌ 
  │ │ ├──[🔀] title (2152:22)
  │ │ └──[➕] default (2153:24)❌ 
  │ └─┬keyspace
  │   ├──[➕] type (2132:21)❌ 
  │   ├──[🔀] title (2133:22)
  │   ├──[➖] anyOf (1:26681)❌ 
  │   └──[➖] anyOf (1:26701)❌ 
  ├─┬WorkflowJobType
  │ └──[➕] enum (7474:11)
  └─┬DestinationConnectorType
    └──[➕] enum (3816:11)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |
| paths            | 10            | 3                |
| components       | 356           | 207              |


</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

---------

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Jordan Homan <jordan@unstructured.io>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/16e0c8159aa07f60f346209d2661d329>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/875f5043b32383cb8853d3dfa9fa5717>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
<details>
<summary>OpenAPI Change Summary</summary>


```
└─┬Info
  └──[🔀] version (18:16)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |


</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

---------

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Co-authored-by: Jordan Homan <jordan@unstructured.io>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/22e5ea0a87c022e301ddc0b9479c97bc>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/37746683fbc81629886ad5fa3bdcde18>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
## Python SDK Changes:
* `unstructured_client.destinations.create_destination()`: 
*
`request.create_destination_connector.config.[open_search_connector_config_input]`
**Added**
  *  `response.config.[open_search_connector_config]` **Added**
* `unstructured_client.destinations.get_destination()`:
`response.config.[open_search_connector_config]` **Added**
* `unstructured_client.destinations.list_destinations()`: 
  *  `request.destination_type` **Changed**
  *  `response.[].config.[open_search_connector_config]` **Added**
* `unstructured_client.destinations.update_destination()`: 
*
`request.update_destination_connector.config.[open_search_connector_config_input]`
**Added**
  *  `response.config.[open_search_connector_config]` **Added**
* `unstructured_client.sources.create_source()`: 
*
`request.create_source_connector.config.[open_search_connector_config_input]`
**Added**
  *  `response.config.[open_search_connector_config]` **Added**
* `unstructured_client.sources.get_source()`:
`response.config.[open_search_connector_config]` **Added**
* `unstructured_client.sources.list_sources()`: 
  *  `request.source_type` **Changed**
  *  `response.[].config.[open_search_connector_config]` **Added**
* `unstructured_client.sources.update_source()`: 
*
`request.update_source_connector.config.[open_search_connector_config_input]`
**Added**
  *  `response.config.[open_search_connector_config]` **Added**

<details>
<summary>OpenAPI Change Summary</summary>


```
└─┬Components
  ├──[➕] schemas (5420:36)
  ├──[➕] schemas (5518:41)
  ├─┬UpdateDestinationConnector
  │ └─┬config
  │   ├──[➕] anyOf (7295:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5519:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5773:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5845:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6135:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6040:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6266:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6724:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7563:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4249:17)❌ 
  ├─┬SourceConnectorType
  │ └──[➕] enum (7091:11)
  ├─┬UpdateSourceConnector
  │ └─┬config
  │   ├──[➕] anyOf (7376:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5519:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5681:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5950:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6396:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6495:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6597:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6875:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4444:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (7831:17)❌ 
  ├─┬CreateSourceConnector
  │ └─┬config
  │   ├──[➕] anyOf (3195:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5519:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5681:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5950:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6396:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6495:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6597:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6875:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (4444:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (7831:17)❌ 
  ├─┬DestinationConnectorType
  │ └──[➕] enum (3840:11)
  ├─┬DestinationConnectorInformation
  │ └─┬config
  │   ├──[➕] anyOf (3787:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5421:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5745:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5802:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6069:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6012:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6204:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6652:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7534:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4181:17)❌ 
  ├─┬CreateDestinationConnector
  │ └─┬config
  │   ├──[➕] anyOf (3105:15)
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5519:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5773:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (5845:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6135:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6040:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6266:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (6724:17)❌ 
  │   ├─┬ANYOF
  │   │ └──[🔀] $ref (7563:17)❌ 
  │   └─┬ANYOF
  │     └──[🔀] $ref (4249:17)❌ 
  └─┬SourceConnectorInformation
    └─┬config
      ├──[➕] anyOf (7041:15)
      ├─┬ANYOF
      │ └──[🔀] $ref (5421:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (5617:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (5888:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (6329:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (6464:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (6543:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (6796:17)❌ 
      ├─┬ANYOF
      │ └──[🔀] $ref (4324:17)❌ 
      └─┬ANYOF
        └──[🔀] $ref (7784:17)❌ 
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| components       | 64            | 54               |


</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
Resolves CVE-2025-66471 and CVE-2025-66418

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Addresses dependency security and stability updates.
> 
> - **Upgrade** `urllib3` from `2.5.0` to `2.6.2` in `poetry.lock`
(includes extras changes)
> - **Bump** package version to `0.42.7` in `pyproject.toml`
> - **Test fix**: add Python 3.9 workaround in
`_test_contract/conftest.py` to eagerly import
`unstructured_client.utils.retries` to avoid a lazy-import race
`KeyError`
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e46c9cb. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Upgrade & regen**
> 
> - Bumps Python SDK to `0.42.7` and updates `__user_agent__`/locks
(`gen.yaml`, `.speakeasy/*.lock`)
> - Regenerates models/docs; minor union order tweaks across destination
models
> 
> **AstraDB connector models**
> 
> - Adds pydantic `extra="allow"` with `__pydantic_extra__` and
`additional_properties` accessors for `AstraDBConnectorConfig` and
`AstraDBConnectorConfigInput`
> - Serialization updated to include additional fields; docs reflect new
`__pydantic_extra__`
> 
> **Docs & samples**
> 
> - Updates `docs/sdks/destinations/README.md` and `codeSamples.yaml` to
use `operations.*` typed request objects for create/update destination
examples
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e91d423. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/3ed620997acae18a1e26ead1df0f8446>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/31f32cae2ab448877e4dff6f510a4fd1>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
<details open>
<summary>OpenAPI Change Summary</summary>
No specification changes
</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
> [!IMPORTANT]
> Linting report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/linting-report/acc9ed4ac70b9e127a4693cc52193c86>
> OpenAPI Change report available at:
<https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/3ce57b3ff042c75da24df753a4e9de1f>
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)
<details>
<summary>OpenAPI Change Summary</summary>


```
└─┬Info
  └──[🔀] version (18:16)
```

| Document Element | Total Changes | Breaking Changes |
|------------------|---------------|------------------|
| info             | 1             | 0                |


</details>

## PYTHON CHANGELOG
No relevant generator changes


Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

Co-authored-by: speakeasybot <bot@speakeasyapi.dev>
This PR replaces `pypdf` with `pypdfium2` when splitting a pdf file into
chunks. This a more robust and faster library, avoiding occasional
`RecursionError` that can happen with `pypdf`.
# SDK update
## Versioning

Version Bump Type: [patch] - 🤖 (automated)


[View full SDK
changelog](https://app.speakeasy.com/org/unstructured/unstructured5xr/changes-report/5a3cd247d1de374a010b2d312a09a5e4)
<details>
<summary>OpenAPI Change Summary</summary>



Based on [Speakeasy CLI](https://github.com/speakeasy-api/speakeasy)
1.601.0

Co-authored-by: Austin Walker <austin@unstructured.io>
## Summary
- Adds `httpx.RemoteProtocolError` to the list of retriable exceptions
in `retries.py` (both sync and async paths)
- When `retry_connection_errors=True`, server disconnects mid-request
(e.g. "Server disconnected without sending a response") are now retried
with backoff, matching the existing behavior for `ConnectError` and
`TimeoutException`
- Previously, `RemoteProtocolError` fell through to the catch-all
`Exception` handler and was wrapped as `PermanentError`, immediately
failing without retry

## Context
When a server crashes mid-request (e.g. SIGSEGV from thread-unsafe
native library access), the client receives an
`httpx.RemoteProtocolError("Server disconnected without sending a
response.")`. Despite `retry_connection_errors=True` being configured,
this error was not retried because the SDK only handled `ConnectError`
and `TimeoutException` as retriable transport errors.

The httpx exception hierarchy is:
```
RemoteProtocolError → ProtocolError → TransportError → RequestError → HTTPError
ConnectError → TransportError → RequestError → HTTPError  (already retried)
TimeoutException → TransportError → RequestError → HTTPError  (already retried)
```

`RemoteProtocolError` is the same class of transient transport error as
the already-retried exceptions.

## Test plan
- [x] Added unit tests for sync and async retry paths
- [ ] `RemoteProtocolError` retried when `retry_connection_errors=True`
— succeeds on 2nd attempt
- [ ] `RemoteProtocolError` raises immediately when
`retry_connection_errors=False`
- [ ] Existing `ConnectError` retry behavior preserved

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes retry behavior for mid-request disconnects, which can increase
duplicate-request risk for non-idempotent operations when
`retry_connection_errors=True`. Scope is limited to transport-error
handling and covered by new unit tests for sync/async paths.
> 
> **Overview**
> **Retries now treat `httpx.RemoteProtocolError` as a retriable
transport failure** when `retry_connection_errors=True`, aligning it
with existing `ConnectError`/`TimeoutException` handling in both `retry`
and `retry_async`.
> 
> Adds unit tests validating the new sync/async retry behavior (and the
disabled case), and bumps the SDK version to `0.42.11` with
corresponding changelog/release entries and user-agent/version updates.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
a7dc972. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- Adds missing trailing spaces to `- OpenAPI Doc` line in the 0.42.11
RELEASES.md entry
- Adds missing trailing newline at end of file
- The Speakeasy publish action failed with `error parsing last release
info` because the format didn't match the expected pattern

This unblocks the 0.42.11 PyPI publish.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: documentation-only formatting tweaks (trailing
spaces/newline) to satisfy Speakeasy release parsing; no runtime code
changes.
> 
> **Overview**
> Fixes the `2026-03-25` (`v0.42.11`) entry in `RELEASES.md` to match
Speakeasy’s expected release format by restoring the trailing spaces on
`- OpenAPI Doc` and ensuring the file ends with a proper final newline
(so the last `PyPI v0.42.11` line is parsed correctly).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
39fd2ae. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tc.) (#334)

## Summary
- Replaces individual `except` blocks for `ConnectError`,
`RemoteProtocolError`, and `TimeoutException` with a single catch for
their parent class `httpx.TransportError`
- This covers `ReadError` (TCP connection reset mid-response with empty
message), `WriteError`, and all other transport-level failures
- Previously, `ReadError` fell through to the catch-all `Exception`
handler and was wrapped as `PermanentError`, failing immediately without
retry

## Context
Follow-up to #332. After deploying the `RemoteProtocolError` fix, we
observed `httpx.ReadError` (empty message) failures when api pods
crashed mid-response. The TCP connection was reset during the response
read phase, which httpx classifies as `ReadError` rather than
`RemoteProtocolError`.

The httpx exception hierarchy:
```
TransportError
├── ConnectError          (was retried)
├── RemoteProtocolError   (was retried since #332)
├── ReadError             (was NOT retried — now fixed)
├── WriteError            (was NOT retried — now fixed)
├── PoolTimeout           (was NOT retried — now fixed)
└── ...
TimeoutException          (was retried, subclass of TransportError)
├── ConnectTimeout
├── ReadTimeout
├── WriteTimeout
└── PoolTimeout
```

Catching `TransportError` is the correct level — all transport errors
are transient and should be retried when `retry_connection_errors=True`.

## Test plan
- [x] Parametrized tests for all TransportError subclasses (sync +
async)
- [ ] Each subclass retried when `retry_connection_errors=True`
- [ ] Each subclass raises immediately when
`retry_connection_errors=False`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Expands which network failures are treated as retryable, which can
change error/latency behavior for callers and potentially mask
persistent transport issues until backoff is exhausted.
> 
> **Overview**
> **Broadened retry handling for transport failures.** The retry wrapper
now catches `httpx.TransportError` in both sync and async paths, so
additional transport-level errors (e.g. `ReadError`, `WriteError`, and
timeout subclasses) are retried when `retry_connection_errors=True`
instead of being treated as permanent.
> 
> Tests were updated to parameterize across multiple `TransportError`
subclasses for both sync and async retry behavior, and the package
version/release notes were bumped to `0.42.12`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bdd403c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Medium risk because it changes packaging/build tooling and CI
execution (Poetry→uv, setuptools build) and adjusts split-PDF hook
timeout/cleanup behavior, which can affect test stability and request
handling.
> 
> **Overview**
> Migrates the project from Poetry to `uv`: CI now installs via `uv sync
--locked`, the Makefile runs lint/tests with `uv run`, and publishing is
switched to `uv build`/`uv publish` with a hardened `scripts/publish.sh`
(strict bash + Python >=3.11 guard). Python support is narrowed to 3.11+
(CI matrix and `pylintrc`), dependency versions are updated, and
`poetry.lock`/`poetry.toml` are removed in favor of a setuptools-based
`pyproject.toml` with dynamic versioning.
> 
> Improves split-PDF behavior and test robustness: the split hook now
propagates request timeouts into chunk requests, scales the outer future
timeout by concurrency “waves”, and ensures per-operation state is
cleaned up on both success and dummy-request failures; corresponding
unit/integration tests were updated (including relaxed equivalence
checks for `hi_res` OCR outputs and longer client timeouts). Adds
regression-guard unit tests to enforce key packaging/CI/publish
invariants and multipart file serialization, and removes an
unused/disabled encryption test suite.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3e0d3d3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Touches core split-PDF execution and retry/timeout cleanup logic;
mistakes could impact partition reliability or leak resources, though
changes are well-covered by expanded unit/integration tests and logging.
> 
> **Overview**
> Improves split-PDF correctness and debuggability by adding
**operation-aware observability** (plan/batch/chunk lifecycle logs) and
propagating split metadata via `X-Unstructured-Split-*` headers into
errors/logs.
> 
> Hardens split execution: per-operation state is isolated, transport
exceptions/cancellations are handled explicitly (with optional
partial-results behavior via `split_pdf_allow_failed`), and
timeout/cleanup paths now safely cancel in-flight work even when event
loops are closed.
> 
> Preserves chunk-level transport retries by deriving a split-specific
retry config that always retries `httpx.TransportError` for chunk calls,
even when SDK-level connection retries are disabled. CI/test tooling is
updated (new platform integration job/target, more verbose integration
output, and bumped GitHub Action versions), and the package is released
as `0.43.1`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e65ce5b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Updated pypi trusted publishing settings too:
<img width="821" height="590" alt="image"
src="https://github.com/user-attachments/assets/0c058ea8-d4e9-4ca3-ba2e-17253e89a16f"
/>



<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Moderate risk because it rewires the release/publish pipeline
(trigger, permissions, artifact flow, and version gating), which could
break publishing if misconfigured. It reduces secret-handling risk by
removing reliance on a long-lived `PYPI_TOKEN`.
> 
> **Overview**
> Switches PyPI releases from the Speakeasy publish workflow +
`PYPI_TOKEN` secret to a GitHub Releases-triggered pipeline that
**builds with `uv`**, validates the release tag matches
`unstructured_client._version`, and **publishes via trusted publishing
(OIDC)** using `pypa/gh-action-pypi-publish`.
> 
> Removes PyPI publishing configuration from Speakeasy
(`.speakeasy/workflow*.yaml`) and stops passing `pypi_token` into the
SDK generation workflow, while bumping SDK/package versioning to
`0.43.2` (generator config + `_version.py`) and adding regression tests
that enforce the new release workflow invariants.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4d38845. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…n error while initiating partition request from unstructured_client", "message": "", "type": "partition_connection_error"} (#338)

504: Error in partition - {"error": "Connection error while initiating
partition request from unstructured_client", "message": "", "type":
"partition_connection_error"}


change: "ReadError after 5 retries over 312.4s" instead of "message": ""

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Behavior only changes error formatting/raising when the backoff budget
is exhausted; normal retry and success paths are unchanged.
> 
> **Overview**
> Improves observability when retries exceed `max_elapsed_time` by
rethrowing the original exception type with a message that includes the
number of attempts and total elapsed time (sync and async backoff
paths), while preserving the original exception via chaining.
> 
> Bumps the generated SDK version/user-agent from `0.43.2` to `0.43.3`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
400e8e5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes core request/hook dispatch and split-PDF async
execution/cancellation paths, which could affect concurrency behavior
and cleanup semantics under load. Added tests mitigate risk, but
regressions could surface in real-world event-loop/thread-local hook
usage.
> 
> **Overview**
> **Fixes `partition_async()` split-PDF execution to be truly async.**
Split chunk collection now runs via awaited async hook dispatch rather
than spinning up a nested event loop in a worker thread, with response
reassembly offloaded so it doesn’t block the event loop.
> 
> **Adds robust cancellation and resource cleanup.** Introduces async
hook dispatch in `SDKHooks` (sync hooks run in `to_thread` with per-hook
serialization), adds cancellation-aware cleanup in
`BaseSDK.do_request_async`, and hardens `SplitPdfHook` with process-wide
PDF-split setup locking, lazy sync executor creation, chunk-task
cancellation/draining, and cleanup of tempdirs/unconsumed chunk files.
Extensive new unit tests cover ordering, concurrency limits,
cancellation, strict-failure behavior, and non-blocking guarantees.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
da0352c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Replace the typed unions in `CreateSourceConnectorConfig`,
`CreateDestinationConnectorConfig`, `UpdateSourceConnectorConfig`,
`UpdateDestinationConnectorConfig`, and the `*ConnectorInformation`
configs with `Dict[str, Any]`.
- Delete the per-connector config models and their docs (e.g.
`S3SourceConnectorConfig`, `AzureDestinationConnectorConfig`,
`OpenSearchConnectorConfig`, …).
- Bump version to 0.44.0 and add a breaking-change changelog entry.

This decouples the SDK from backend connector schemas — adding/removing
fields on a connector no longer requires an SDK release.

These were deprecated in our docs last year. None of our SDK snippets
use them.

## Test plan
- [ ] CI green
- [ ] Smoke: create a source connector with a plain dict config against
SND and confirm the request goes through

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> This is a breaking change to the SDK surface area: connector `config`
fields switch from typed models/unions to `Dict[str, Any]`, and many
generated connector config classes/docs are removed, which may break
downstream type checks and runtime imports.
> 
> **Overview**
> **Decouples connector configuration from generated SDK models.**
Connector `config` fields for create/update and `*ConnectorInformation`
responses now use `Dict[str, Any]` instead of per-connector typed
unions/models.
> 
> Removes the generated connector config model classes and their
documentation, updates contract tests to assert `config` is a `dict`,
and bumps the SDK version to `0.44.0` with a breaking-change entry in
`CHANGELOG.md` (plus release metadata in `RELEASES.md`).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
eb0ae92. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ackoffStrategy (#342)

## Summary

`retry_with_backoff_async` currently checks `now - start >
max_elapsed_time` *before* sleeping. When `RetryConfig.max_elapsed_time`
is set to e.g. 5 min and the per-attempt httpx client timeout is 30 min,
any attempt that runs longer than the budget blows it on attempt 1 —
**zero retries fire on subsequent transient errors**. Closes that
short-circuit without re-introducing the unbounded retry loops that
recent budget tightening was meant to prevent.

## What changes

New optional fields on `BackoffStrategy` (default values preserve
existing behavior):

- `min_attempts: int = 0` — minimum retry attempts that must fire before
`max_elapsed_time` is honored. Counts retries, not the initial attempt.
`min_attempts=2` permits 1 initial + at least 2 retries (3 total
attempts) before the soft cap can cut the loop.
- `absolute_max_elapsed_time_ms: int | None = None` — cap on when a new
retry can START. Does NOT interrupt an in-flight `func()` call.
Worst-case wall-clock under this cap is `absolute_max_elapsed_time_ms +
per_attempt_timeout`.

Loop changes in both sync (`retry_with_backoff`) and async
(`retry_with_backoff_async`) paths:

1. **Post-attempt cap check.** Soft cap honored only when `retries >=
min_attempts`; hard cap unconditional.
2. **Pre-sleep hard-cap check.** Refuses to sleep into a retry whose
projected start would cross the hard cap.
3. **Post-sleep verification.** Belt-and-suspenders against late wakeups
and projection rounding.
4. **Helper extraction** (`_cap_hit_after_attempt`,
`_raise_or_return_after_cap`) dedupes the soft/hard cap logic between
sync and async.

Validation in `BackoffStrategy.__init__` rejects `min_attempts < 0`,
`absolute_max_elapsed_time_ms <= 0`, and hard cap below soft cap.

`.genignore` updated to preserve these fields across future Speakeasy
regens, matching the `general.py` and `users.py` precedent.

## Design choices

- **Hard cap is a retry-start cap, not a wall-clock bound.** In-flight
`func()` cannot be interrupted from the retry loop. Consumers should
pair the cap with a sensible per-attempt timeout to keep the worst case
bounded.
- **Defaults preserve existing behavior** so this change is non-breaking
for every existing consumer. Consumers opt in by setting `min_attempts >
0` and/or `absolute_max_elapsed_time_ms`.

## Tests

49 tests pass (46 unit + 3 split-PDF retry integration). New coverage:

- **T1–T14** in `_test_unstructured_client/unit/test_retries.py`:
fake-clock harness monkeypatching `time.time` / `time.sleep` /
`asyncio.sleep` / `random.uniform`. Covers the slow-first-attempt +
`min_attempts` floor scenario, floor-is-not-a-ceiling semantics, hard
cap overrides floor, sleep truncation, `TemporaryError` early-return
through both caps, `PermanentError` short-circuit immunity, and
`BackoffStrategy.__init__` validation.
-
**`test_split_pdf_cache_tmp_data_chunk_request_stream_is_replay_safe`**
in `integration/test_decorators.py`: pins the body-replay invariant for
chunk requests built from open file objects (the
`split_pdf_cache_tmp_data=True` path). Iterates `request.stream` twice
directly — bypasses `request.read()` caching — so a future Speakeasy
template change that produced a single-consumption stream would fail
this test.

## Test plan

- [x] `uv run pytest _test_unstructured_client/unit/test_retries.py` —
46 pass
- [x] `uv run pytest
_test_unstructured_client/integration/test_decorators.py::test_split_pdf_*retry*
_test_unstructured_client/integration/test_decorators.py::test_split_pdf_cache_tmp_data_*`
— 3 pass
- [ ] CI green
## Summary

Cuts a 0.44.1 release so the `BackoffStrategy` retry-budget fields that
landed on main in #342 actually ship to PyPI. v0.44.0 was tagged before
#342 merged, so PyPI v0.44.0 does not include those fields.

## Changes

- `src/unstructured_client/_version.py`: bump `__version__` and
`__user_agent__` to `0.44.1`.
- `CHANGELOG.md`: split the combined 0.44.0 entry — `min_attempts` /
`absolute_max_elapsed_time_ms` move under a new `## 0.44.1` section so
the changelog matches what's actually in each PyPI artifact.
- `RELEASES.md`: append a 0.44.1 entry following the existing
Speakeasy-publish format.

## Test plan

- [x] No code changes; only metadata files
- [ ] CI green
- [ ] PyPI publish workflow picks up the bump on merge
#344)

## What

Adds env-var knobs for the `httpx.AsyncClient` used by
`split_pdf_hook.run_tasks`, and ships them as **0.45.0**. Defaults match
httpx — fully backward compatible.

### Connection-pool limits
- `UNSTRUCTURED_CLIENT_MAX_CONNECTIONS` (default `100`)
- `UNSTRUCTURED_CLIENT_MAX_KEEPALIVE_CONNECTIONS` (default `20`)
- `UNSTRUCTURED_CLIENT_KEEPALIVE_EXPIRY` (default `5.0` seconds)

### TLS trust store (server verification)
Honors the standard env vars other Python tooling already respects, so a
single setting applies uniformly:
- `SSL_CERT_FILE` (stdlib `ssl` convention)
- `REQUESTS_CA_BUNDLE` (requests / httpx-ecosystem convention; used if
`SSL_CERT_FILE` is unset)

### mTLS client certificate
- `UNSTRUCTURED_CLIENT_TLS_CLIENT_CERT` — PEM file (httpx reads key from
the same file by default)
- `UNSTRUCTURED_CLIENT_TLS_CLIENT_KEY` — optional, when cert and key
live in separate files

### Observability
- Extends the existing `split_pdf event=plan_created` INFO log to
include the resolved pool values and trust-store / mTLS mode, so the
active config is visible in production logs without leaking filesystem
paths.

### Release
- Bumps `_version.py` to `0.45.0`, adds a `0.45.0` `CHANGELOG.md`
section, and appends a matching `RELEASES.md` entry.

## Why

When the SDK runs in an environment where load balancing happens at
TCP-connect time rather than per-request (a common Kubernetes setup with
a plain ClusterIP and no service mesh), httpx's default keepalive
pooling can lock onto a subset of backends. Newly added backends never
receive traffic because existing connections stay glued to the
originally-resolved set.

Letting operators force shorter keepalive (e.g.
`MAX_KEEPALIVE_CONNECTIONS=1` + a low `KEEPALIVE_EXPIRY`) makes the
client re-establish connections more frequently, redistributing across
the available backends.

The TLS additions are for SDK consumers running behind corporate proxies
with custom CAs, or against backends that require mTLS — previously they
had to subclass / monkey-patch to get a custom `verify` or `cert` into
the split-PDF client.

## How to use

```yaml
env:
  # Pool reshuffling for connect-time-only LBs
  - name: UNSTRUCTURED_CLIENT_MAX_KEEPALIVE_CONNECTIONS
    value: "1"
  - name: UNSTRUCTURED_CLIENT_KEEPALIVE_EXPIRY
    value: "30.0"

  # Custom trust store (standard env var, picked up by httpx, requests, ssl)
  - name: SSL_CERT_FILE
    value: /etc/ssl/internal-ca-bundle.pem

  # mTLS
  - name: UNSTRUCTURED_CLIENT_TLS_CLIENT_CERT
    value: /etc/ssl/client.crt
  - name: UNSTRUCTURED_CLIENT_TLS_CLIENT_KEY
    value: /etc/ssl/client.key
```

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## What

Adds an opt-in NDJSON response mode to `partition()` that returns
elements as a **path to a file on disk** instead of a parsed list, and
ships it as **0.46.0**.

```python
from unstructured_client.general import PartitionAcceptEnum

res = client.general.partition(
    request=req,
    accept_header_override=PartitionAcceptEnum.APPLICATION_X_NDJSON,
)

try:
    with open(res.elements_file, encoding="utf-8") as f:
        for line in f:
            element = json.loads(line)
            ...
finally:
    os.unlink(res.elements_file)
```

`PartitionResponse.elements_file` is set instead of
`PartitionResponse.elements`. **The caller owns the file and must delete
it.** Requesting `application/json` remains the default and is entirely
unchanged.

## Why

On the split-PDF path the SDK rebuilt the whole document in memory in
order to return it: a list per chunk, a flattened list, a `json.dumps`
blob in `create_response`, and then the SDK's re-parse of that blob —
four copies live at once, with the serialization step dominating peak
usage. For documents with large `metadata.image_base64` payloads this is
the difference between a job completing and being OOM-killed.

In the new mode the per-chunk temp files are concatenated on disk and
never parsed, so peak memory is roughly one chunk rather than the whole
document.

## How

- `combine_chunk_files_to_ndjson` concatenates chunk files on disk. Each
chunk is sniffed for its first non-whitespace character, so a server
returning `application/json` still works; chunks that are already NDJSON
are copied through without parsing.
- `ndjson_mode` depends **only** on the `Accept` header, never on
`split_pdf_cache_tmp_data`. Those are set by different parties, so
gating on both let them disagree — the server would return NDJSON while
the hook took the JSON path and `res.json()` raised on a body this
client had itself requested.
- Both caching modes are handled. A cached chunk contributes its
existing temp-file path; an uncached one spills its body verbatim and
then **releases** it, since every response is retained in
`api_successful_responses` and leaving `_content` set would keep the
document resident regardless.
- The combined file is deliberately written outside the operation's
`TemporaryDirectory`, which `_clear_operation` removes as soon as
`after_success` returns.

## Temp-file ownership

Everything this path creates is accounted for:

- Spilled chunk bodies are written inside the operation's temp directory
and unlinked once combined.
- The combined file is deleted when a chunk failure means it is never
handed back to the caller.
- Recombination writes to a staging file that is atomically renamed into
place only on success, so a malformed chunk cannot orphan a partial
file.
- No combined file is created at all when every chunk failed.

## Security

The elements-file marker is an **httpx response extension**, not a
response header. Extensions are populated by the transport, so a remote
server cannot set the key. A header would be wire-controlled, and since
callers are documented to open `elements_file` and then delete it, that
would hand a hostile server an arbitrary local file to destroy. A real
server body is always copied to a file this client creates.

## Regeneration

`elements_file` is client-side only and can never come from the OpenAPI
spec, so a regeneration would silently drop it. Both `general.py` and
`models/operations/partition.py` are now in `.genignore`, and
`test_regeneration_guards.py` fails if either entry is lost.

## Known limitation

`elements_file` is set for every input, so callers need one code path.
The **memory saving**, however, applies only to split PDFs.

An input is sent whole when it is not a PDF, when
`split_pdf_page=False`, or when it has two pages or fewer —
`_before_request_unlocked` short-circuits on `split_size >= page_count`
and `get_optimal_split_size` floors at `MIN_PAGES_PER_SPLIT = 2`. For
those, the body is read fully into memory before being written to disk,
so peak is roughly 2x the body rather than bounded.

Bounding it means `stream=True` for NDJSON requests, which makes
`raw_response.content` raise on the returned closed response — a
user-visible change worth its own review. Tracked separately.

Note also that the deployed API does not currently emit
`application/x-ndjson`, so the unsplit path reaches the JSON-to-NDJSON
conversion rather than the streamed-body branch. That is not merely a
spec omission: the service does not negotiate the response format on
`Accept` at all. It selects the format from the `output_format` form
field, and consults `Accept` only to choose `multipart/mixed` and to
reject conflicting media types on multi-file uploads. NDJSON was
therefore never going to arrive via `Accept`.

The service's `406 NOT_ACCEPTABLE` on an unrecognized `Accept` is gated
on multi-file uploads. This SDK sends a single file per request —
`PartitionParameters.files` is one `Files`, and the split-PDF hook sends
one chunk per request — so that branch is unreachable from here and the
unsplit path cannot raise `SDKError` because of it. Server-side NDJSON
support is tracked separately.

## Testing

- New `_test_unstructured_client/unit/test_ndjson_elements_file.py` —
recombination across JSON-array / NDJSON / mixed chunk formats, order
preservation, byte-exact payload round-trip, non-ASCII, temp-file
lifecycle on success and failure, and regression guards for the
header-spoofing and partial-output defects.
- 235 unit tests and 64 contract tests pass; `pylint` 10.00/10; `mypy`
clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/Unstructured-IO/unstructured-python-client/pull/347?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…348)

## The bug

In NDJSON elements-file mode, a split-PDF chunk that returned HTTP 200
with an empty body was logged and skipped, so the combined
`elements_file` was silently short by that chunk's pages while the call
still returned 200.

Nothing downstream could detect it: `combine_chunk_files_to_ndjson`
hands back only the combined path, and `split_pdf_allow_failed=False`
does not cover the case because an empty 200 counts as a *successful*
chunk. The buffered path fails outright on the same response
(`res.json()` raises `JSONDecodeError`), so enabling NDJSON mode
converted a hard failure into silent truncation.

## The fix

Recombination now raises `EmptyChunkResponseError` (a `ValueError`,
matching where the buffered path's `JSONDecodeError` lands) instead of
skipping.

Emptiness is judged against the chunk's own `Content-Type`, because the
two formats disagree about what an empty body means:

- **JSON** has no empty document — a chunk with no elements is `[]` — so
an empty body is malformed and raises.
- **`application/x-ndjson`** encodes zero records as zero lines, so an
empty body is well formed. It contributes nothing and does not fail the
partition; otherwise a split whose pages are blank would break once a
server honors the accept header.
- An **unknown or missing** media type is read as JSON. That is what the
deployed API returns, and guessing NDJSON would reinstate the silent
truncation.

`combine_chunk_files_to_ndjson` takes an optional `media_types` list,
positionally matched to `chunk_paths`; omitting it keeps the strict
reading. `_elements_from_task_responses` collects each chunk's
`Content-Type` before the cached branch overwrites the body with a
temp-file path — the header survives both cache branches, so the cached
case carries a real media type too.

## Tests

`_test_unstructured_client/unit/test_ndjson_elements_file.py`:

- empty JSON chunk raises, over 3 empty-ish bodies × `application/json`
/ unset / with-charset
- empty NDJSON chunk is zero records, over 3 bodies × 3 media-type
spellings (parameters, casing)
- every chunk empty yields an empty output rather than an error
- `ValueError` parity with the buffered path, and the strict default
when `media_types` is omitted
- hook level, both cache modes: an empty JSON chunk fails the operation
and leaves no partial, spilled, or combined file behind; an empty NDJSON
chunk is accepted with the surrounding chunks' elements intact

Mutating `_is_ndjson_media_type` to `return False` fails 12 of these,
including both hook-level cache-mode tests, so the coverage is
load-bearing.

271 unit + 64 contract tests pass; pylint 10/10 on both changed modules.

## Release

Bumped to 0.46.1 with CHANGELOG and RELEASES entries. Consumers that pin
`unstructured-client >=0.46.0` for NDJSON elements-file mode should
raise the floor to `>=0.46.1`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bumps `anthropics/claude-code-action` from `@beta` to `@v1`.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/Unstructured-IO/unstructured-python-client/pull/351?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
## What & why

**Problem:** The Transform Platform's API Keys page hands you
`https://platform-api.transform.unstructured.io/api/v1`, and the docs
tell you to pass that value as `server_url`. Do it and every Platform
call in this SDK fails with a 404: listing jobs, creating a workflow,
checking a connector. The same URL works with curl, so the URL looks
right and the SDK looks broken, and there is nothing in the error to
point at the real cause. Anyone starting from the app's own copy button
hits this on their first call.

**Change:** Treat hosts under `unstructured.io` as Unstructured API
hosts, so a copied `/api/v1` suffix is stripped from `server_url` the
way it already was for `unstructuredapp.io`. Also clean the base URL for
an operation-level `server_url=` override, which bypassed the cleaning
hook entirely.

## Linked ticket

none

Client-facing follow-up: reported while writing the Transform Python
quickstart, where every SDK sample had to be written against a URL
different from the one the app displays.

## The bug

Every Platform operation in this SDK already carries its own path
prefix. `jobs.list_jobs` requests `/api/v1/jobs/`,
`workflows.create_workflow` requests `/api/v1/workflows/`, and so on. So
the base URL must not carry `/api/v1` of its own.

`clean_server_url` exists to strip exactly that kind of pasted-in path,
but it only did so when the host contained `unstructuredapp.io`:

```python
if "unstructuredapp.io" in parsed_url.netloc:
    ...
    clean_url = urlunparse(parsed_url._replace(path="", ...))
else:
    # For other domains, we want to keep the path
    clean_url = urlunparse(parsed_url._replace(params="", query="", fragment=""))
```

`platform-api.transform.unstructured.io` does not match, so the path was
kept and the operation path was appended on top, giving
`/api/v1/api/v1/jobs/`, which matches no route.

`basesdk.py` is generated, so the customization needs protecting: it is
now in `.genignore`, the mechanism this repo already uses for
`general.py`, `users.py`, `retries.py` and `partition.py`, with a guard
test alongside the existing ones asserting that both the `_get_url` call
and the `.genignore` entry survive. Without it a regeneration silently
drops the fix and the doubled prefix returns. Freezing the file freezes
the generated request, retry and hook plumbing too, so the entry carries
the same un-freeze procedure `general.py` documents.

Three smaller problems came out of the same code while fixing it. The
host test was a substring match, so `unstructuredapp.io.example.com` was
treated as one of ours and had its path stripped and its scheme forced
to HTTPS; it is now matched on domain boundaries and left alone. A
`server_url=` passed to a single operation never reached the cleaning
hook at all, because the hook runs at SDK init; that override is now
cleaned in `BaseSDK._get_url`, the one point every operation's base URL
passes through. And a fully qualified host carrying the terminal root
dot (`api.unstructuredapp.io.`) has to be recognized explicitly, since
the old substring test matched it by accident and the domain-boundary
test does not; the path is stripped as before and the host keeps its
dot, which changes the Host header and SNI and is the caller's choice to
make.

## What the patch changes, and what it does not

Every `server_url` shape the existing tests, the docs and the app use,
run through `clean_server_url` on `main` and on this branch. Seven
results change; sixteen are byte-identical.

| `server_url` | `main` | this branch | |
| --- | --- | --- | --- |
| `https://platform-api.transform.unstructured.io/api/v1` |
`https://platform-api.transform.unstructured.io/api/v1` |
`https://platform-api.transform.unstructured.io` | changed |
| `http://platform-api.transform.unstructured.io/api/v1` |
`http://platform-api.transform.unstructured.io/api/v1` |
`https://platform-api.transform.unstructured.io` | changed |
| `platform-api.transform.unstructured.io/api/v1` |
`http://platform-api.transform.unstructured.io/api/v1` |
`https://platform-api.transform.unstructured.io` | changed |
| `platform-api.transform.unstructured.io` |
`http://platform-api.transform.unstructured.io` |
`https://platform-api.transform.unstructured.io` | changed |
| `https://platform-api.unstructured.io/api/v1` |
`https://platform-api.unstructured.io/api/v1` |
`https://platform-api.unstructured.io` | changed |
| `http://unstructuredapp.io.example.com/api/v1` |
`https://unstructuredapp.io.example.com` |
`http://unstructuredapp.io.example.com/api/v1` | changed |
| `http://myunstructuredapp.io/api/v1` | `https://myunstructuredapp.io`
| `http://myunstructuredapp.io/api/v1` | changed |
| `https://platform-api.transform.unstructured.io` |
`https://platform-api.transform.unstructured.io` | same | |
| `https://platform.unstructuredapp.io/api/v1` |
`https://platform.unstructuredapp.io` | same | |
| `https://api.unstructuredapp.io/general/v0/general` |
`https://api.unstructuredapp.io` | same | |
| `unstructured-000mock.api.unstructuredapp.io/general/v0/general` |
`https://unstructured-000mock.api.unstructuredapp.io` | same | |
| `http://localhost:8000` | `http://localhost:8000` | same | |
| `localhost:8000` | `http://localhost:8000` | same | |
| `http://localhost:8000/my/endpoint/` |
`http://localhost:8000/my/endpoint` | same | |
| `localhost:8000/general/v0/general` |
`http://localhost:8000/general/v0/general` | same | |
| `https://unstructured.example.com/api/v1` |
`https://unstructured.example.com/api/v1` | same | |
| `http://not-unstructured.io/api/v1` |
`http://not-unstructured.io/api/v1` | same | |

The first five changed rows are the reported bug. The last two are the
substring-match fix: those hosts are not ours, so they keep their path
and their scheme.

## Impact

**Customers:** Anyone using the Python SDK against the Transform
Platform can now paste the API URL shown in the app, or set it from the
documented `UNSTRUCTURED_API_URL`, and have jobs, workflows, sources,
destinations and templates calls work. Today that exact value 404s on
every call. Users who already worked around it by passing the bare host
are unaffected; that keeps working. Users on `unstructuredapp.io` are
unaffected; their URLs were already cleaned.

**Internal (devs / ops / other teams):** The docs can stop steering
readers away from the URL the product displays. No service imports this
code; it is a client library published to PyPI.

**Wire contract / clients:** No request or response shape changes. The
only behavior change is which URL a request is sent to, and only for
base URLs that were previously producing a doubled path. The one case
where a user could notice a difference is a self-hosted deployment on a
host under `unstructuredapp.io` or `unstructured.io` that genuinely
serves the API beneath a subpath; that path is now stripped. Hosts
outside those domains keep their path exactly as before, which the
existing localhost subpath tests cover.

**Deployment target considerations:** This is a PyPI client library, not
a deployed service, so SaaS / DI / in-VPC / on-prem / SND deploys are
unaffected. Air-gapped users pointing the SDK at their own hostname keep
the existing keep-the-path behavior, since their host is not under an
Unstructured domain.

## A note on the diff size

The last commit is `ruff format` over the files this change touches,
plus seven `noqa` directives for pre-existing lint that cannot be
auto-fixed without changing behaviour. It is formatting only and carries
no behaviour change, so reading the first two commits on their own gives
you the whole fix. Two of the `noqa`s are worth knowing about: `raise
err` in `basesdk.py` re-raises whatever an after-error hook returned,
which is not always the active exception, so ruff's suggested bare
`raise` would be a real bug.

## Risk / rollback

Low. Small changes to URL normalization plus a `.genignore` entry,
revert-safe, no migration and no flag.

## How it was verified

Ran the unit suite on Python 3.11, 3.12 and 3.13 and the contract suite,
plus `pylint` (10.00/10) and `mypy`, all green, matching what CI runs.
`uv.lock` is unchanged, so the `UV_LOCKED=1` install holds. Reproduced
the bug and then the fix against the live Transform Platform API without
an API key, which is enough to tell the two apart: a route that exists
answers 401, a route that does not answers 404. Not exercised with a
real API key end to end, and not exercised against a self-hosted
deployment.

## Proof

Repro, against the live API, before the fix:

```
$ curl -s -o /dev/null -w '%{http_code}\n' https://platform-api.transform.unstructured.io/api/v1/jobs/
401
$ curl -s -o /dev/null -w '%{http_code}\n' https://platform-api.transform.unstructured.io/api/v1/api/v1/jobs/
404
```

Through the SDK, before the fix:

```
server_url='https://platform-api.transform.unstructured.io'
  request sent: https://platform-api.transform.unstructured.io/api/v1/jobs/
  status:       401

server_url='https://platform-api.transform.unstructured.io/api/v1'
  request sent: https://platform-api.transform.unstructured.io/api/v1/api/v1/jobs/
  status:       404
```

Failing tests at `HEAD` before the fix,
`_test_unstructured_client/unit/test_server_urls.py::test_platform_request_url_has_a_single_api_prefix`
plus the hook tests:

```
FAILED test_custom_hooks.py::test_unit_clean_server_url_fixes_malformed_transform_platform_url[https://platform-api.transform.unstructured.io/api/v1]
FAILED test_custom_hooks.py::test_unit_clean_server_url_fixes_malformed_transform_platform_url[http://platform-api.transform.unstructured.io/api/v1]
FAILED test_custom_hooks.py::test_unit_clean_server_url_fixes_malformed_transform_platform_url[platform-api.transform.unstructured.io/api/v1]
FAILED test_custom_hooks.py::test_unit_clean_server_url_fixes_malformed_transform_platform_url[platform-api.transform.unstructured.io]
FAILED test_custom_hooks.py::test_unit_clean_server_url_leaves_lookalike_domains_alone[http://unstructuredapp.io.example.com/api/v1]

E  Failed: transform platform ... Expected https://platform-api.transform.unstructured.io, got https://platform-api.transform.unstructured.io/api/v1
```

After the fix, the same live check across both ways of passing the URL:

```
client-level, bare host
  sent:   https://platform-api.transform.unstructured.io/api/v1/jobs/
  status: 401

client-level, URL from the app (/api/v1)
  sent:   https://platform-api.transform.unstructured.io/api/v1/jobs/
  status: 401

operation-level, bare host
  sent:   https://platform-api.transform.unstructured.io/api/v1/jobs/
  status: 401

operation-level, URL from the app (/api/v1)
  sent:   https://platform-api.transform.unstructured.io/api/v1/jobs/
  status: 401
```

Every case now reaches the real route. Suites after the fix: unit and
contract both pass, `pylint` 10.00/10, `mypy` clean.

## Dependencies / merge order

none

## Worked Example

```python
from unstructured_client import UnstructuredClient

# The value the app's API Keys page gives you, pasted as-is.
client = UnstructuredClient(
    api_key_auth="YOUR_KEY",
    server_url="https://platform-api.transform.unstructured.io/api/v1",
)

client.jobs.list_jobs(request={})
# before: GET https://platform-api.transform.unstructured.io/api/v1/api/v1/jobs/ -> 404 {"detail":"Not Found"}
# after:  GET https://platform-api.transform.unstructured.io/api/v1/jobs/, the real route
```

## Release

Bumped to 0.46.2 with CHANGELOG and RELEASES entries.

---------

Co-authored-by: paulkarayan <pk@unstructured.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.


Back | FazBrowse Home | New Git URL