Skip to content

Commit 08c4b8b

Browse files
Merge pull request #27 from Zipstack/feat/generated-transport
UN-4010 [FEAT] Support every API deployment request parameter via a generated transport
2 parents 79f8d09 + ea4e9ac commit 08c4b8b

35 files changed

Lines changed: 6032 additions & 219 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/unstract/api_deployments/_sdk_docstudio/** linguist-generated=true

.github/workflows/test.yml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ jobs:
1414
matrix:
1515
python-version: ["3.11", "3.12"]
1616
steps:
17-
- uses: actions/checkout@v4
17+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
1818

19-
- uses: actions/setup-python@v5
19+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
2020
with:
2121
python-version: ${{ matrix.python-version }}
2222

2323
- name: Install uv
24-
uses: astral-sh/setup-uv@v6
24+
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
2525
with:
2626
version: "0.6.14"
2727
enable-cache: true
@@ -37,3 +37,31 @@ jobs:
3737

3838
- name: Tests (pytest)
3939
run: uv run pytest tests/ -v
40+
41+
sdk-drift:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
45+
46+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
47+
with:
48+
python-version: "3.12"
49+
50+
- name: Install uv
51+
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
52+
with:
53+
version: "0.6.14"
54+
enable-cache: true
55+
56+
# The generated tree is committed, so an edit to it reviews like any
57+
# other change and then disappears on the next regeneration. Same for a
58+
# spec change that never had the generator run over it.
59+
- name: Regenerate from the committed spec
60+
run: ./tools/gen_sdk.sh
61+
62+
# `git add -N` first: a diff alone cannot see a file the generator has
63+
# newly created, which is exactly what a spec growing an endpoint does.
64+
- name: Fail if the committed SDK is not what the spec generates
65+
run: |
66+
git add -N -- src/unstract/api_deployments/_sdk_docstudio
67+
git diff --exit-code -- src/unstract/api_deployments/_sdk_docstudio

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ celerybeat.pid
114114
# Environments
115115
.env
116116
.venv
117+
# Built inside the repo by tools/gen_sdk.sh.
118+
.gen-venv/
117119
env/
118120
venv/
119121
ENV/

README.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,28 @@ except APIDeploymentsClientException as e:
6565

6666
`api_url`: The URL of the Unstract API deployment.
6767
`api_key`: Your raw API key. **Do not** include the `"Bearer "` prefix — the client adds it automatically.
68-
`api_timeout`: Set a timeout for API requests, e.g., `api_timeout=10`.
68+
`api_timeout`: Backend execution mode sent with the request (see `timeout` on `structure_file`). `0` or below queues the execution and returns immediately; above it the call runs synchronously and the value bounds how long the backend waits. This is not a socket timeout — pass `transport_timeout` for that.
6969
`logging_level`: Set logging verbosity (e.g., "`DEBUG`").
7070
`include_metadata`: If set to `True`, the response will include additional metadata (cost, tokens consumed and context) for each call made by the Prompt Studio exported tool.
71+
`transport_timeout`: Socket timeout in seconds (keyword-only). Left unset, a stalled connection blocks forever, which is what earlier releases did.
72+
73+
## Closing the client
74+
75+
The client reuses connections between calls, so release them when you are done
76+
with it — either by calling `close()`, or by using it as a context manager:
77+
78+
```python
79+
with APIDeploymentsClient(api_url="url", api_key="your_api_key") as adc:
80+
response = adc.structure_file(["<file>"])
81+
```
82+
83+
A long-lived client can be left open; one built per job should be closed.
7184

7285
## Retry Configuration
7386

7487
The client includes built-in exponential backoff retry with the following behavior:
7588

76-
- **Async mode** (`api_timeout=0`): POST requests are retried on transient failures (5xx, 429) and connection errors, since the server returns immediately after queuing.
89+
- **Async mode** (`api_timeout` of `0` or below): POST requests are retried on transient failures (5xx, 429) and connection errors, since the server returns immediately after queuing.
7790
- **Sync mode** (`api_timeout > 0`, the default): POST requests are **not** retried, because the server blocks during processing — a failure may mean the request was processed but the response was lost.
7891
- **Status polling** (`check_execution_status`): GET requests are always retried, as they are idempotent.
7992

@@ -100,16 +113,24 @@ client = APIDeploymentsClient(
100113
The retry logic uses exponential backoff with full jitter and respects the `Retry-After` header on 429 responses.
101114

102115

103-
## Unstract CLI
116+
## Internals
117+
118+
`unstract.api_deployments._sdk_docstudio` is generated from the deployment API's
119+
OpenAPI spec by `tools/gen_sdk.sh` and is an implementation detail of the
120+
transport. `APIDeploymentsClient` is the supported surface — import from it, not
121+
from the generated tree, which is regenerated wholesale whenever the spec moves.
122+
123+
## Cloning an organization
104124

105-
Installing `unstract-client` also provides the `unstract` command:
125+
Installing `unstract-client` also provides a clone command. This package no
126+
longer installs an `unstract` console script, so invoke it as a module:
106127

107128
```bash
108129
pip install unstract-client
109-
unstract --help
130+
python -m unstract.clone --help
110131
```
111132

112-
### `unstract clone`
133+
### `python -m unstract.clone clone`
113134

114135
Clones an organization's resources to another org, on the same or a different
115136
deployment (e.g. promote **dev****QA****prod**). Covers adapters,
@@ -124,7 +145,7 @@ so keys never land in shell history:
124145
export UNSTRACT_SRC_PLATFORM_KEY="<source platform key>"
125146
export UNSTRACT_TGT_PLATFORM_KEY="<target platform key>"
126147

127-
unstract clone \
148+
python -m unstract.clone clone \
128149
--source-url https://dev.example.com --source-org org_dev123 \
129150
--target-url https://qa.example.com --target-org org_qa456 \
130151
--dry-run
@@ -152,14 +173,14 @@ failed run can be resumed by re-running the same command.
152173

153174
#### Compatibility
154175

155-
`unstract clone` is capability-probed: each phase checks for its endpoint on the
176+
Cloning is capability-probed: each phase checks for its endpoint on the
156177
source and target, and clones only what both orgs support. A capability missing on
157178
either side is reported and skipped — the run never fails because of a version
158179
difference. Cloning a newer source into an older target therefore drops the entity
159180
types the target lacks (listed in the end-of-run report).
160181

161182
- Run the source and target on the same (or a newer-target) Unstract build.
162-
- Use `unstract-client >= 1.4.0`, the first release that ships `unstract clone`.
183+
- Use `unstract-client >= 1.4.0`, the first release that ships the clone command.
163184

164185
## Questions and Feedback
165186

pyproject.toml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ authors = [
66
{name = "Zipstack Inc", email = "devsupport@zipstack.com"},
77
]
88
dependencies = [
9+
# The transport layer is generated against httpx; attrs backs its models.
10+
# Upper-bounded because the generated code is written against one minor
11+
# series: a bump has to be regenerated and re-tested, not resolved into.
12+
"httpx>=0.27,<0.29",
13+
"attrs>=23.2",
14+
# Still the transport for the `unstract.clone` subpackage, and the source of
15+
# the exception classes callers catch by name around the deployment client.
916
"requests>=2.32.3",
1017
"tenacity>=8.2.0",
1118
"click>=8.1",
@@ -27,9 +34,6 @@ classifiers = [
2734
"Topic :: Software Development :: Libraries :: Python Modules",
2835
]
2936

30-
[project.scripts]
31-
unstract = "unstract.cli:main"
32-
3337
[build-system]
3438
requires = ["hatchling"]
3539
build-backend = "hatchling.build"
@@ -64,6 +68,9 @@ lint = [
6468

6569
[tool.ruff]
6670
line-length = 88
71+
# Generated and vendored code is overwritten wholesale by its refresh script, so
72+
# a lint finding there can never be fixed in place.
73+
extend-exclude = ["src/unstract/api_deployments/_sdk_docstudio", "tests/baseline"]
6774

6875
[tool.ruff.lint]
6976
select = ["E", "F", "W", "I"]

0 commit comments

Comments
 (0)