parallel-web 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of parallel-web might be problematic. Click here for more details.
- parallel_web-0.1.2/.release-please-manifest.json +3 -0
- parallel_web-0.1.2/CHANGELOG.md +76 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/CONTRIBUTING.md +3 -4
- {parallel_web-0.1.0 → parallel_web-0.1.2}/PKG-INFO +24 -20
- {parallel_web-0.1.0 → parallel_web-0.1.2}/README.md +17 -13
- {parallel_web-0.1.0 → parallel_web-0.1.2}/SECURITY.md +2 -2
- {parallel_web-0.1.0 → parallel_web-0.1.2}/pyproject.toml +6 -5
- {parallel_web-0.1.0 → parallel_web-0.1.2}/requirements-dev.lock +4 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/__init__.py +5 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_base_client.py +22 -2
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_models.py +2 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_types.py +2 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_proxy.py +4 -1
- parallel_web-0.1.2/src/parallel/_utils/_resources_proxy.py +24 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_version.py +1 -1
- parallel_web-0.1.2/src/parallel/lib/_pydantic.py +31 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_time.py +19 -2
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/resources/task_run.py +4 -4
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run.py +1 -4
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/conftest.py +2 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_client.py +116 -41
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_utils/test_proxy.py +11 -0
- parallel_web-0.1.0/.release-please-manifest.json +0 -3
- parallel_web-0.1.0/CHANGELOG.md +0 -16
- parallel_web-0.1.0/src/parallel/lib/_pydantic.py +0 -29
- {parallel_web-0.1.0 → parallel_web-0.1.2}/.gitignore +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/LICENSE +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/api.md +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/bin/check-release-environment +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/bin/publish-pypi +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/examples/.keep +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/mypy.ini +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/noxfile.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/release-please-config.json +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/requirements.lock +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_client.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_compat.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_constants.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_exceptions.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_files.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_qs.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_resource.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_response.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_streaming.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_logs.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_reflection.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_streams.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_sync.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_transform.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_typing.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_utils.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/.keep +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_run_result.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_spec.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/py.typed +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/resources/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/json_schema_param.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/parsed_task_run_result.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_create_params.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_result.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_result_params.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_spec_param.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/text_schema_param.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/api_resources/__init__.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/api_resources/test_task_run.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/sample_file.txt +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_deepcopy.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_extract_files.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_files.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_models.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_qs.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_required_args.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_response.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_streaming.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_transform.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_utils/test_typing.py +0 -0
- {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/utils.py +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.2 (2025-06-25)
|
|
4
|
+
|
|
5
|
+
Full Changelog: [v0.1.1...v0.1.2](https://github.com/parallel-web/parallel-sdk-python/compare/v0.1.1...v0.1.2)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **api:** add execute method and structured output support ([5e51379](https://github.com/parallel-web/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
10
|
+
* **api:** update via SDK Studio ([7526908](https://github.com/parallel-web/parallel-sdk-python/commit/752690867c75ee970582fabc05c939a2f619cb3f))
|
|
11
|
+
* **api:** update via SDK Studio ([6698e71](https://github.com/parallel-web/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
12
|
+
* **client:** add follow_redirects request option ([deff733](https://github.com/parallel-web/parallel-sdk-python/commit/deff733f189070bb471ebd6cbf92dfd61d19734a))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **api:** handle retryable errors ([#2](https://github.com/parallel-web/parallel-sdk-python/issues/2)) ([5317550](https://github.com/parallel-web/parallel-sdk-python/commit/531755070eb4b798a7f0b51153414425a0c293b0))
|
|
18
|
+
* **client:** correctly parse binary response | stream ([9546f27](https://github.com/parallel-web/parallel-sdk-python/commit/9546f276ca2d63cf3c6a9b0eef23f1eed35758fa))
|
|
19
|
+
* **package:** support direct resource imports ([52fe297](https://github.com/parallel-web/parallel-sdk-python/commit/52fe297a34a6a2a473be0f124e2febab1df527fe))
|
|
20
|
+
* **pydantic:** add fields to json schema, better error messages ([38a2ddc](https://github.com/parallel-web/parallel-sdk-python/commit/38a2ddc348ac7acf11f9f75f69900b628e539c1d))
|
|
21
|
+
* **tests:** fix: tests which call HTTP endpoints directly with the example parameters ([bfad009](https://github.com/parallel-web/parallel-sdk-python/commit/bfad009314f4f3ce31265d2be07f091eb7db664a))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Chores
|
|
25
|
+
|
|
26
|
+
* **ci:** enable for pull requests ([0ae47ea](https://github.com/parallel-web/parallel-sdk-python/commit/0ae47eaf080510a886eb40aed7c8189faa940f2c))
|
|
27
|
+
* **ci:** fix installation instructions ([150a642](https://github.com/parallel-web/parallel-sdk-python/commit/150a6429ee584a0c32160be88d9bdcd4eeab4579))
|
|
28
|
+
* **ci:** upload sdks to package manager ([3bd8b36](https://github.com/parallel-web/parallel-sdk-python/commit/3bd8b361b84bad87c0943c2fe71465c92cdea599))
|
|
29
|
+
* **docs:** grammar improvements ([c5b636b](https://github.com/parallel-web/parallel-sdk-python/commit/c5b636bfeb60b02f84f5b9e93687359cd9c5c251))
|
|
30
|
+
* **docs:** remove reference to rye shell ([a64869e](https://github.com/parallel-web/parallel-sdk-python/commit/a64869e70e9c493f2dc3e8618327f28544d36058))
|
|
31
|
+
* **docs:** remove unnecessary param examples ([e15712a](https://github.com/parallel-web/parallel-sdk-python/commit/e15712a074ba66a6b0d225bb3a6979a767c15225))
|
|
32
|
+
* **internal:** avoid errors for isinstance checks on proxies ([4149fb9](https://github.com/parallel-web/parallel-sdk-python/commit/4149fb963b39db2211f404f94bf7b55a57c2556b))
|
|
33
|
+
* **internal:** codegen related update ([6a0bb66](https://github.com/parallel-web/parallel-sdk-python/commit/6a0bb662f5011bbea13f75334eb55c5144b50e8b))
|
|
34
|
+
* **internal:** update conftest.py ([0e08356](https://github.com/parallel-web/parallel-sdk-python/commit/0e0835661e91993042605131065729d006761a5a))
|
|
35
|
+
* **readme:** update badges ([36c14b5](https://github.com/parallel-web/parallel-sdk-python/commit/36c14b529ec8611508b6b7cc9065c67e59e5ecdc))
|
|
36
|
+
* **readme:** update low level api examples ([f17e34e](https://github.com/parallel-web/parallel-sdk-python/commit/f17e34e0e0a6d3205c344c278f1643826938e9d1))
|
|
37
|
+
* **tests:** add tests for httpx client instantiation & proxies ([d84ffff](https://github.com/parallel-web/parallel-sdk-python/commit/d84ffff48a814edc81ef62249353053df6398c90))
|
|
38
|
+
* **tests:** run tests in parallel ([62252c6](https://github.com/parallel-web/parallel-sdk-python/commit/62252c6f1098ad138978b6efa1fc2a9c22961040))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
* **client:** fix httpx.Timeout documentation reference ([17f87ee](https://github.com/parallel-web/parallel-sdk-python/commit/17f87eef5af2b06b3791f9218b7ab4f9098faf9c))
|
|
44
|
+
|
|
45
|
+
## 0.1.1 (2025-04-25)
|
|
46
|
+
|
|
47
|
+
Full Changelog: [v0.1.0...v0.1.1](https://github.com/shapleyai/parallel-sdk-python/compare/v0.1.0...v0.1.1)
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
* **api:** update via SDK Studio ([4cc79c4](https://github.com/shapleyai/parallel-sdk-python/commit/4cc79c4d1edaa9d1d080b81830961252c8b327c1))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Bug Fixes
|
|
55
|
+
|
|
56
|
+
* **pydantic:** add fields to json schema, better error messages ([38a2ddc](https://github.com/shapleyai/parallel-sdk-python/commit/38a2ddc348ac7acf11f9f75f69900b628e539c1d))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Chores
|
|
60
|
+
|
|
61
|
+
* **readme:** update low level api examples ([f17e34e](https://github.com/shapleyai/parallel-sdk-python/commit/f17e34e0e0a6d3205c344c278f1643826938e9d1))
|
|
62
|
+
|
|
63
|
+
## 0.1.0 (2025-04-24)
|
|
64
|
+
|
|
65
|
+
Full Changelog: [v0.0.1-alpha.0...v0.1.0](https://github.com/shapleyai/parallel-sdk-python/compare/v0.0.1-alpha.0...v0.1.0)
|
|
66
|
+
|
|
67
|
+
### Features
|
|
68
|
+
|
|
69
|
+
* **api:** add execute method and structured output support ([5e51379](https://github.com/shapleyai/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
70
|
+
* **api:** update via SDK Studio ([c393d04](https://github.com/shapleyai/parallel-sdk-python/commit/c393d048bddb554c37eb750ca57c4335243a70ed))
|
|
71
|
+
* **api:** update via SDK Studio ([6698e71](https://github.com/shapleyai/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Chores
|
|
75
|
+
|
|
76
|
+
* go live ([061677a](https://github.com/shapleyai/parallel-sdk-python/commit/061677a22549f3dd3d9f4591c9ccfdf71209c12e))
|
|
@@ -17,8 +17,7 @@ $ rye sync --all-features
|
|
|
17
17
|
You can then run scripts using `rye run python script.py` or by activating the virtual environment:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
|
|
21
|
-
# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work
|
|
20
|
+
# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
|
|
22
21
|
$ source .venv/bin/activate
|
|
23
22
|
|
|
24
23
|
# now you can omit the `rye run` prefix
|
|
@@ -63,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g
|
|
|
63
62
|
To install via git:
|
|
64
63
|
|
|
65
64
|
```sh
|
|
66
|
-
$ pip install git+ssh://git@github.com/
|
|
65
|
+
$ pip install git+ssh://git@github.com/parallel-web/parallel-sdk-python.git
|
|
67
66
|
```
|
|
68
67
|
|
|
69
68
|
Alternatively, you can build from source and install the wheel file:
|
|
@@ -121,7 +120,7 @@ the changes aren't made through the automated pipeline, you may want to make rel
|
|
|
121
120
|
|
|
122
121
|
### Publish with a GitHub workflow
|
|
123
122
|
|
|
124
|
-
You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/
|
|
123
|
+
You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/parallel-web/parallel-sdk-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
|
|
125
124
|
|
|
126
125
|
### Publish manually
|
|
127
126
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: parallel-web
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: The official Python library for the Parallel API
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/parallel-web/parallel-sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/parallel-web/parallel-sdk-python
|
|
7
7
|
Author-email: Parallel <support@parallel.ai>
|
|
8
8
|
License: MIT
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# Parallel Python API library
|
|
33
33
|
|
|
34
|
-
[](https://pypi.org/project/parallel-web/)
|
|
34
|
+
[>)](https://pypi.org/project/parallel-web/)
|
|
35
35
|
|
|
36
36
|
The Parallel Python library provides convenient access to the Parallel REST API from any Python 3.8+
|
|
37
37
|
application. The library includes type definitions for all request params and response fields,
|
|
@@ -43,7 +43,7 @@ It is generated with [Stainless](https://www.stainless.com/).
|
|
|
43
43
|
## Documentation
|
|
44
44
|
|
|
45
45
|
The REST API documentation can be found on our [docs](https://docs.parallel.ai).
|
|
46
|
-
The full API of this Python library can be found in [api.md](https://github.com/
|
|
46
|
+
The full API of this Python library can be found in [api.md](https://github.com/parallel-web/parallel-sdk-python/tree/main/api.md).
|
|
47
47
|
|
|
48
48
|
## Installation
|
|
49
49
|
|
|
@@ -54,7 +54,7 @@ pip install parallel-web
|
|
|
54
54
|
|
|
55
55
|
## Usage
|
|
56
56
|
|
|
57
|
-
The full API of this library can be found in [api.md](https://github.com/
|
|
57
|
+
The full API of this library can be found in [api.md](https://github.com/parallel-web/parallel-sdk-python/tree/main/api.md).
|
|
58
58
|
|
|
59
59
|
```python
|
|
60
60
|
import os
|
|
@@ -223,15 +223,15 @@ from parallel.types import TaskSpecParam
|
|
|
223
223
|
client = Parallel()
|
|
224
224
|
|
|
225
225
|
task_run = client.task_run.create(
|
|
226
|
-
input="France
|
|
227
|
-
processor="
|
|
226
|
+
input={"country": "France", "year": 2023},
|
|
227
|
+
processor="core",
|
|
228
228
|
task_spec={
|
|
229
229
|
"output_schema": {
|
|
230
230
|
"json_schema": {
|
|
231
231
|
"additionalProperties": False,
|
|
232
232
|
"properties": {
|
|
233
233
|
"gdp": {
|
|
234
|
-
"description": "GDP in USD for the year
|
|
234
|
+
"description": "GDP in USD for the year",
|
|
235
235
|
"type": "string",
|
|
236
236
|
}
|
|
237
237
|
},
|
|
@@ -244,12 +244,16 @@ task_run = client.task_run.create(
|
|
|
244
244
|
"json_schema": {
|
|
245
245
|
"additionalProperties": False,
|
|
246
246
|
"properties": {
|
|
247
|
-
"
|
|
248
|
-
"description": "
|
|
247
|
+
"country": {
|
|
248
|
+
"description": "Name of the country to research",
|
|
249
249
|
"type": "string",
|
|
250
|
-
}
|
|
250
|
+
},
|
|
251
|
+
"year": {
|
|
252
|
+
"description": "Year for which to retrieve information",
|
|
253
|
+
"type": "integer",
|
|
254
|
+
},
|
|
251
255
|
},
|
|
252
|
-
"required": ["
|
|
256
|
+
"required": ["country", "year"],
|
|
253
257
|
"type": "object",
|
|
254
258
|
},
|
|
255
259
|
"type": "json",
|
|
@@ -257,7 +261,7 @@ task_run = client.task_run.create(
|
|
|
257
261
|
},
|
|
258
262
|
)
|
|
259
263
|
|
|
260
|
-
run_result = client.task_run.result(task_run.
|
|
264
|
+
run_result = client.task_run.result(task_run.run_id)
|
|
261
265
|
print(run_result.output)
|
|
262
266
|
```
|
|
263
267
|
|
|
@@ -339,7 +343,7 @@ client.with_options(max_retries=5).task_run.execute(
|
|
|
339
343
|
### Timeouts
|
|
340
344
|
|
|
341
345
|
By default requests time out after 1 minute. You can configure this with a `timeout` option,
|
|
342
|
-
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
|
|
346
|
+
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
|
|
343
347
|
|
|
344
348
|
```python
|
|
345
349
|
from parallel import Parallel
|
|
@@ -365,7 +369,7 @@ client.with_options(timeout=5.0).task_run.execute(
|
|
|
365
369
|
|
|
366
370
|
On timeout, an `APITimeoutError` is thrown.
|
|
367
371
|
|
|
368
|
-
Note that requests that time out are [retried twice by default](https://github.com/
|
|
372
|
+
Note that requests that time out are [retried twice by default](https://github.com/parallel-web/parallel-sdk-python/tree/main/#retries).
|
|
369
373
|
|
|
370
374
|
## Advanced
|
|
371
375
|
|
|
@@ -412,9 +416,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
|
|
|
412
416
|
print(task_run.output)
|
|
413
417
|
```
|
|
414
418
|
|
|
415
|
-
These methods return an [`APIResponse`](https://github.com/
|
|
419
|
+
These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
|
|
416
420
|
|
|
417
|
-
The async client returns an [`AsyncAPIResponse`](https://github.com/
|
|
421
|
+
The async client returns an [`AsyncAPIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
|
|
418
422
|
|
|
419
423
|
#### `.with_streaming_response`
|
|
420
424
|
|
|
@@ -522,7 +526,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
|
|
|
522
526
|
|
|
523
527
|
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
|
|
524
528
|
|
|
525
|
-
We are keen for your feedback; please open an [issue](https://www.github.com/
|
|
529
|
+
We are keen for your feedback; please open an [issue](https://www.github.com/parallel-web/parallel-sdk-python/issues) with questions, bugs, or suggestions.
|
|
526
530
|
|
|
527
531
|
### Determining the installed version
|
|
528
532
|
|
|
@@ -541,4 +545,4 @@ Python 3.8 or higher.
|
|
|
541
545
|
|
|
542
546
|
## Contributing
|
|
543
547
|
|
|
544
|
-
See [the contributing documentation](https://github.com/
|
|
548
|
+
See [the contributing documentation](https://github.com/parallel-web/parallel-sdk-python/tree/main/./CONTRIBUTING.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Parallel Python API library
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/parallel-web/)
|
|
3
|
+
[>)](https://pypi.org/project/parallel-web/)
|
|
4
4
|
|
|
5
5
|
The Parallel Python library provides convenient access to the Parallel REST API from any Python 3.8+
|
|
6
6
|
application. The library includes type definitions for all request params and response fields,
|
|
@@ -192,15 +192,15 @@ from parallel.types import TaskSpecParam
|
|
|
192
192
|
client = Parallel()
|
|
193
193
|
|
|
194
194
|
task_run = client.task_run.create(
|
|
195
|
-
input="France
|
|
196
|
-
processor="
|
|
195
|
+
input={"country": "France", "year": 2023},
|
|
196
|
+
processor="core",
|
|
197
197
|
task_spec={
|
|
198
198
|
"output_schema": {
|
|
199
199
|
"json_schema": {
|
|
200
200
|
"additionalProperties": False,
|
|
201
201
|
"properties": {
|
|
202
202
|
"gdp": {
|
|
203
|
-
"description": "GDP in USD for the year
|
|
203
|
+
"description": "GDP in USD for the year",
|
|
204
204
|
"type": "string",
|
|
205
205
|
}
|
|
206
206
|
},
|
|
@@ -213,12 +213,16 @@ task_run = client.task_run.create(
|
|
|
213
213
|
"json_schema": {
|
|
214
214
|
"additionalProperties": False,
|
|
215
215
|
"properties": {
|
|
216
|
-
"
|
|
217
|
-
"description": "
|
|
216
|
+
"country": {
|
|
217
|
+
"description": "Name of the country to research",
|
|
218
218
|
"type": "string",
|
|
219
|
-
}
|
|
219
|
+
},
|
|
220
|
+
"year": {
|
|
221
|
+
"description": "Year for which to retrieve information",
|
|
222
|
+
"type": "integer",
|
|
223
|
+
},
|
|
220
224
|
},
|
|
221
|
-
"required": ["
|
|
225
|
+
"required": ["country", "year"],
|
|
222
226
|
"type": "object",
|
|
223
227
|
},
|
|
224
228
|
"type": "json",
|
|
@@ -226,7 +230,7 @@ task_run = client.task_run.create(
|
|
|
226
230
|
},
|
|
227
231
|
)
|
|
228
232
|
|
|
229
|
-
run_result = client.task_run.result(task_run.
|
|
233
|
+
run_result = client.task_run.result(task_run.run_id)
|
|
230
234
|
print(run_result.output)
|
|
231
235
|
```
|
|
232
236
|
|
|
@@ -308,7 +312,7 @@ client.with_options(max_retries=5).task_run.execute(
|
|
|
308
312
|
### Timeouts
|
|
309
313
|
|
|
310
314
|
By default requests time out after 1 minute. You can configure this with a `timeout` option,
|
|
311
|
-
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
|
|
315
|
+
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
|
|
312
316
|
|
|
313
317
|
```python
|
|
314
318
|
from parallel import Parallel
|
|
@@ -381,9 +385,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
|
|
|
381
385
|
print(task_run.output)
|
|
382
386
|
```
|
|
383
387
|
|
|
384
|
-
These methods return an [`APIResponse`](https://github.com/
|
|
388
|
+
These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
|
|
385
389
|
|
|
386
|
-
The async client returns an [`AsyncAPIResponse`](https://github.com/
|
|
390
|
+
The async client returns an [`AsyncAPIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
|
|
387
391
|
|
|
388
392
|
#### `.with_streaming_response`
|
|
389
393
|
|
|
@@ -491,7 +495,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
|
|
|
491
495
|
|
|
492
496
|
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
|
|
493
497
|
|
|
494
|
-
We are keen for your feedback; please open an [issue](https://www.github.com/
|
|
498
|
+
We are keen for your feedback; please open an [issue](https://www.github.com/parallel-web/parallel-sdk-python/issues) with questions, bugs, or suggestions.
|
|
495
499
|
|
|
496
500
|
### Determining the installed version
|
|
497
501
|
|
|
@@ -16,11 +16,11 @@ before making any information public.
|
|
|
16
16
|
## Reporting Non-SDK Related Security Issues
|
|
17
17
|
|
|
18
18
|
If you encounter security issues that are not directly related to SDKs but pertain to the services
|
|
19
|
-
or products provided by Parallel please follow the respective company's security reporting guidelines.
|
|
19
|
+
or products provided by Parallel, please follow the respective company's security reporting guidelines.
|
|
20
20
|
|
|
21
21
|
### Parallel Terms and Policies
|
|
22
22
|
|
|
23
|
-
Please contact support@parallel.ai for any questions or concerns regarding security of our services.
|
|
23
|
+
Please contact support@parallel.ai for any questions or concerns regarding the security of our services.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "parallel-web"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "The official Python library for the Parallel API"
|
|
5
5
|
dynamic = ["readme"]
|
|
6
6
|
license = "MIT"
|
|
@@ -34,8 +34,8 @@ classifiers = [
|
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
[project.urls]
|
|
37
|
-
Homepage = "https://github.com/
|
|
38
|
-
Repository = "https://github.com/
|
|
37
|
+
Homepage = "https://github.com/parallel-web/parallel-sdk-python"
|
|
38
|
+
Repository = "https://github.com/parallel-web/parallel-sdk-python"
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
[tool.rye]
|
|
@@ -54,6 +54,7 @@ dev-dependencies = [
|
|
|
54
54
|
"importlib-metadata>=6.7.0",
|
|
55
55
|
"rich>=13.7.1",
|
|
56
56
|
"nest_asyncio==1.6.0",
|
|
57
|
+
"pytest-xdist>=3.6.1",
|
|
57
58
|
]
|
|
58
59
|
|
|
59
60
|
[tool.rye.scripts]
|
|
@@ -121,11 +122,11 @@ path = "README.md"
|
|
|
121
122
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
|
122
123
|
# replace relative links with absolute links
|
|
123
124
|
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
|
|
124
|
-
replacement = '[\1](https://github.com/
|
|
125
|
+
replacement = '[\1](https://github.com/parallel-web/parallel-sdk-python/tree/main/\g<2>)'
|
|
125
126
|
|
|
126
127
|
[tool.pytest.ini_options]
|
|
127
128
|
testpaths = ["tests"]
|
|
128
|
-
addopts = "--tb=short"
|
|
129
|
+
addopts = "--tb=short -n auto"
|
|
129
130
|
xfail_strict = true
|
|
130
131
|
asyncio_mode = "auto"
|
|
131
132
|
asyncio_default_fixture_loop_scope = "session"
|
|
@@ -30,6 +30,8 @@ distro==1.8.0
|
|
|
30
30
|
exceptiongroup==1.2.2
|
|
31
31
|
# via anyio
|
|
32
32
|
# via pytest
|
|
33
|
+
execnet==2.1.1
|
|
34
|
+
# via pytest-xdist
|
|
33
35
|
filelock==3.12.4
|
|
34
36
|
# via virtualenv
|
|
35
37
|
h11==0.14.0
|
|
@@ -72,7 +74,9 @@ pygments==2.18.0
|
|
|
72
74
|
pyright==1.1.399
|
|
73
75
|
pytest==8.3.3
|
|
74
76
|
# via pytest-asyncio
|
|
77
|
+
# via pytest-xdist
|
|
75
78
|
pytest-asyncio==0.24.0
|
|
79
|
+
pytest-xdist==3.7.0
|
|
76
80
|
python-dateutil==2.8.2
|
|
77
81
|
# via time-machine
|
|
78
82
|
pytz==2023.3.post1
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
|
+
import typing as _t
|
|
4
|
+
|
|
3
5
|
from . import types
|
|
4
6
|
from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
|
|
5
7
|
from ._utils import file_from_path
|
|
@@ -78,6 +80,9 @@ __all__ = [
|
|
|
78
80
|
"DefaultAsyncHttpxClient",
|
|
79
81
|
]
|
|
80
82
|
|
|
83
|
+
if not _t.TYPE_CHECKING:
|
|
84
|
+
from ._utils._resources_proxy import resources as resources
|
|
85
|
+
|
|
81
86
|
_setup_logging()
|
|
82
87
|
|
|
83
88
|
# Update the __module__ attribute for exported symbols so that
|
|
@@ -960,6 +960,9 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
960
960
|
if self.custom_auth is not None:
|
|
961
961
|
kwargs["auth"] = self.custom_auth
|
|
962
962
|
|
|
963
|
+
if options.follow_redirects is not None:
|
|
964
|
+
kwargs["follow_redirects"] = options.follow_redirects
|
|
965
|
+
|
|
963
966
|
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
|
|
964
967
|
|
|
965
968
|
response = None
|
|
@@ -1068,7 +1071,14 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
1068
1071
|
) -> ResponseT:
|
|
1069
1072
|
origin = get_origin(cast_to) or cast_to
|
|
1070
1073
|
|
|
1071
|
-
if
|
|
1074
|
+
if (
|
|
1075
|
+
inspect.isclass(origin)
|
|
1076
|
+
and issubclass(origin, BaseAPIResponse)
|
|
1077
|
+
# we only want to actually return the custom BaseAPIResponse class if we're
|
|
1078
|
+
# returning the raw response, or if we're not streaming SSE, as if we're streaming
|
|
1079
|
+
# SSE then `cast_to` doesn't actively reflect the type we need to parse into
|
|
1080
|
+
and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
|
|
1081
|
+
):
|
|
1072
1082
|
if not issubclass(origin, APIResponse):
|
|
1073
1083
|
raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
|
|
1074
1084
|
|
|
@@ -1460,6 +1470,9 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1460
1470
|
if self.custom_auth is not None:
|
|
1461
1471
|
kwargs["auth"] = self.custom_auth
|
|
1462
1472
|
|
|
1473
|
+
if options.follow_redirects is not None:
|
|
1474
|
+
kwargs["follow_redirects"] = options.follow_redirects
|
|
1475
|
+
|
|
1463
1476
|
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
|
|
1464
1477
|
|
|
1465
1478
|
response = None
|
|
@@ -1568,7 +1581,14 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1568
1581
|
) -> ResponseT:
|
|
1569
1582
|
origin = get_origin(cast_to) or cast_to
|
|
1570
1583
|
|
|
1571
|
-
if
|
|
1584
|
+
if (
|
|
1585
|
+
inspect.isclass(origin)
|
|
1586
|
+
and issubclass(origin, BaseAPIResponse)
|
|
1587
|
+
# we only want to actually return the custom BaseAPIResponse class if we're
|
|
1588
|
+
# returning the raw response, or if we're not streaming SSE, as if we're streaming
|
|
1589
|
+
# SSE then `cast_to` doesn't actively reflect the type we need to parse into
|
|
1590
|
+
and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
|
|
1591
|
+
):
|
|
1572
1592
|
if not issubclass(origin, AsyncAPIResponse):
|
|
1573
1593
|
raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
|
|
1574
1594
|
|
|
@@ -737,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
|
|
|
737
737
|
idempotency_key: str
|
|
738
738
|
json_data: Body
|
|
739
739
|
extra_json: AnyMapping
|
|
740
|
+
follow_redirects: bool
|
|
740
741
|
|
|
741
742
|
|
|
742
743
|
@final
|
|
@@ -750,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel):
|
|
|
750
751
|
files: Union[HttpxRequestFiles, None] = None
|
|
751
752
|
idempotency_key: Union[str, None] = None
|
|
752
753
|
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
|
|
754
|
+
follow_redirects: Union[bool, None] = None
|
|
753
755
|
|
|
754
756
|
# It should be noted that we cannot use `json` here as that would override
|
|
755
757
|
# a BaseModel method in an incompatible fashion.
|
|
@@ -100,6 +100,7 @@ class RequestOptions(TypedDict, total=False):
|
|
|
100
100
|
params: Query
|
|
101
101
|
extra_json: AnyMapping
|
|
102
102
|
idempotency_key: str
|
|
103
|
+
follow_redirects: bool
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
# Sentinel class used until PEP 0661 is accepted
|
|
@@ -215,3 +216,4 @@ class _GenericAlias(Protocol):
|
|
|
215
216
|
|
|
216
217
|
class HttpxSendArgs(TypedDict, total=False):
|
|
217
218
|
auth: httpx.Auth
|
|
219
|
+
follow_redirects: bool
|
|
@@ -46,7 +46,10 @@ class LazyProxy(Generic[T], ABC):
|
|
|
46
46
|
@property # type: ignore
|
|
47
47
|
@override
|
|
48
48
|
def __class__(self) -> type: # pyright: ignore
|
|
49
|
-
|
|
49
|
+
try:
|
|
50
|
+
proxied = self.__get_proxied__()
|
|
51
|
+
except Exception:
|
|
52
|
+
return type(self)
|
|
50
53
|
if issubclass(type(proxied), LazyProxy):
|
|
51
54
|
return type(proxied)
|
|
52
55
|
return proxied.__class__
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing_extensions import override
|
|
5
|
+
|
|
6
|
+
from ._proxy import LazyProxy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ResourcesProxy(LazyProxy[Any]):
|
|
10
|
+
"""A proxy for the `parallel.resources` module.
|
|
11
|
+
|
|
12
|
+
This is used so that we can lazily import `parallel.resources` only when
|
|
13
|
+
needed *and* so that users can just import `parallel` and reference `parallel.resources`
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@override
|
|
17
|
+
def __load__(self) -> Any:
|
|
18
|
+
import importlib
|
|
19
|
+
|
|
20
|
+
mod = importlib.import_module("parallel.resources")
|
|
21
|
+
return mod
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
resources = ResourcesProxy().__as_proxied__()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing_extensions import TypeGuard
|
|
6
|
+
|
|
7
|
+
import pydantic
|
|
8
|
+
|
|
9
|
+
from .._compat import PYDANTIC_V2, model_json_schema
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def to_json_schema(
|
|
13
|
+
model_type: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any],
|
|
14
|
+
) -> dict[str, Any]:
|
|
15
|
+
"""Convert a Pydantic model/type adapter to a JSON schema."""
|
|
16
|
+
if is_basemodel_type(model_type):
|
|
17
|
+
schema = model_json_schema(model_type)
|
|
18
|
+
elif isinstance(model_type, pydantic.TypeAdapter):
|
|
19
|
+
if not PYDANTIC_V2:
|
|
20
|
+
raise TypeError(f"TypeAdapters are only supported with Pydantic v2 - {model_type}")
|
|
21
|
+
schema = model_type.json_schema()
|
|
22
|
+
else:
|
|
23
|
+
raise TypeError(f"Unsupported type: {model_type}")
|
|
24
|
+
|
|
25
|
+
# modify the schema to make it compatible with the API format
|
|
26
|
+
schema["additionalProperties"] = False
|
|
27
|
+
return schema
|
|
28
|
+
|
|
29
|
+
def is_basemodel_type(model_type: object) -> TypeGuard[type[pydantic.BaseModel]]:
|
|
30
|
+
"""Check if a type is a Pydantic BaseModel to avoid using type: ignore."""
|
|
31
|
+
return inspect.isclass(model_type) and issubclass(model_type, pydantic.BaseModel)
|
|
@@ -28,6 +28,24 @@ def _raise_timeout(run_id: str, exc: Union[Exception, None]) -> NoReturn:
|
|
|
28
28
|
raise TimeoutError(f"Fetching task run result for run id {run_id} timed out.") from exc
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def _is_retryable_error(status_code: int) -> bool:
|
|
32
|
+
"""Determine if an error is retryable.
|
|
33
|
+
|
|
34
|
+
We retry the following HTTP status codes within the SDK:
|
|
35
|
+
- 408 (Request Timeout): The server timed out waiting for the request
|
|
36
|
+
- 503 (Service Unavailable): The server is temporarily unable to handle the request
|
|
37
|
+
- 504 (Gateway Timeout): The gateway server timed out
|
|
38
|
+
|
|
39
|
+
These errors typically indicate temporary issues with the server or network
|
|
40
|
+
that may resolve upon retry. We don't include 429 (Too Many Requests) as this
|
|
41
|
+
indicates rate limiting, which requires backing off rather than immediate retries.
|
|
42
|
+
|
|
43
|
+
Note: This is a low-level retry mechanism within the SDK. Customers may want to
|
|
44
|
+
implement their own retry logic at the application level for other error types.
|
|
45
|
+
"""
|
|
46
|
+
return status_code in (408, 503, 504)
|
|
47
|
+
|
|
48
|
+
|
|
31
49
|
@contextlib.contextmanager
|
|
32
50
|
def timeout_retry_context(run_id: str, deadline: float) -> Iterator[None]:
|
|
33
51
|
"""Context manager for handling timeouts and retries when fetching task run results.
|
|
@@ -49,8 +67,7 @@ def timeout_retry_context(run_id: str, deadline: float) -> Iterator[None]:
|
|
|
49
67
|
exc = e
|
|
50
68
|
continue
|
|
51
69
|
except APIStatusError as e:
|
|
52
|
-
|
|
53
|
-
if e.status_code == 408:
|
|
70
|
+
if _is_retryable_error(e.status_code):
|
|
54
71
|
exc = e
|
|
55
72
|
continue
|
|
56
73
|
raise
|
|
@@ -42,7 +42,7 @@ class TaskRunResource(SyncAPIResource):
|
|
|
42
42
|
This property can be used as a prefix for any HTTP method call to return
|
|
43
43
|
the raw response object instead of the parsed content.
|
|
44
44
|
|
|
45
|
-
For more information, see https://www.github.com/
|
|
45
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#accessing-raw-response-data-eg-headers
|
|
46
46
|
"""
|
|
47
47
|
return TaskRunResourceWithRawResponse(self)
|
|
48
48
|
|
|
@@ -51,7 +51,7 @@ class TaskRunResource(SyncAPIResource):
|
|
|
51
51
|
"""
|
|
52
52
|
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
53
53
|
|
|
54
|
-
For more information, see https://www.github.com/
|
|
54
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#with_streaming_response
|
|
55
55
|
"""
|
|
56
56
|
return TaskRunResourceWithStreamingResponse(self)
|
|
57
57
|
|
|
@@ -317,7 +317,7 @@ class AsyncTaskRunResource(AsyncAPIResource):
|
|
|
317
317
|
This property can be used as a prefix for any HTTP method call to return
|
|
318
318
|
the raw response object instead of the parsed content.
|
|
319
319
|
|
|
320
|
-
For more information, see https://www.github.com/
|
|
320
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#accessing-raw-response-data-eg-headers
|
|
321
321
|
"""
|
|
322
322
|
return AsyncTaskRunResourceWithRawResponse(self)
|
|
323
323
|
|
|
@@ -326,7 +326,7 @@ class AsyncTaskRunResource(AsyncAPIResource):
|
|
|
326
326
|
"""
|
|
327
327
|
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
328
328
|
|
|
329
|
-
For more information, see https://www.github.com/
|
|
329
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#with_streaming_response
|
|
330
330
|
"""
|
|
331
331
|
return AsyncTaskRunResourceWithStreamingResponse(self)
|
|
332
332
|
|
|
@@ -29,12 +29,9 @@ class TaskRun(BaseModel):
|
|
|
29
29
|
is_active: bool
|
|
30
30
|
"""Whether the run is currently active; i.e.
|
|
31
31
|
|
|
32
|
-
status is one of {'
|
|
32
|
+
status is one of {'running', 'queued', 'cancelling'}.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
message: Optional[str] = None
|
|
36
|
-
"""Human-readable status message for the run."""
|
|
37
|
-
|
|
38
35
|
modified_at: Optional[str] = None
|
|
39
36
|
"""Timestamp of the last modification to the task, as an RFC 3339 string."""
|
|
40
37
|
|
|
@@ -23,17 +23,16 @@ from pydantic import ValidationError
|
|
|
23
23
|
|
|
24
24
|
from parallel import Parallel, AsyncParallel, APIResponseValidationError
|
|
25
25
|
from parallel._types import Omit
|
|
26
|
-
from parallel._utils import maybe_transform
|
|
27
26
|
from parallel._models import BaseModel, FinalRequestOptions
|
|
28
|
-
from parallel._constants import RAW_RESPONSE_HEADER
|
|
29
27
|
from parallel._exceptions import ParallelError, APIStatusError, APITimeoutError, APIResponseValidationError
|
|
30
28
|
from parallel._base_client import (
|
|
31
29
|
DEFAULT_TIMEOUT,
|
|
32
30
|
HTTPX_DEFAULT_TIMEOUT,
|
|
33
31
|
BaseClient,
|
|
32
|
+
DefaultHttpxClient,
|
|
33
|
+
DefaultAsyncHttpxClient,
|
|
34
34
|
make_request_options,
|
|
35
35
|
)
|
|
36
|
-
from parallel.types.task_run_create_params import TaskRunCreateParams
|
|
37
36
|
|
|
38
37
|
from .utils import update_env
|
|
39
38
|
|
|
@@ -713,36 +712,21 @@ class TestParallel:
|
|
|
713
712
|
|
|
714
713
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
715
714
|
@pytest.mark.respx(base_url=base_url)
|
|
716
|
-
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
|
|
715
|
+
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Parallel) -> None:
|
|
717
716
|
respx_mock.post("/v1/tasks/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
|
|
718
717
|
|
|
719
718
|
with pytest.raises(APITimeoutError):
|
|
720
|
-
|
|
721
|
-
"/v1/tasks/runs",
|
|
722
|
-
body=cast(
|
|
723
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
724
|
-
),
|
|
725
|
-
cast_to=httpx.Response,
|
|
726
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
727
|
-
)
|
|
719
|
+
client.task_run.with_streaming_response.create(input="France (2023)", processor="processor").__enter__()
|
|
728
720
|
|
|
729
721
|
assert _get_open_connections(self.client) == 0
|
|
730
722
|
|
|
731
723
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
732
724
|
@pytest.mark.respx(base_url=base_url)
|
|
733
|
-
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
|
|
725
|
+
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Parallel) -> None:
|
|
734
726
|
respx_mock.post("/v1/tasks/runs").mock(return_value=httpx.Response(500))
|
|
735
727
|
|
|
736
728
|
with pytest.raises(APIStatusError):
|
|
737
|
-
|
|
738
|
-
"/v1/tasks/runs",
|
|
739
|
-
body=cast(
|
|
740
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
741
|
-
),
|
|
742
|
-
cast_to=httpx.Response,
|
|
743
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
744
|
-
)
|
|
745
|
-
|
|
729
|
+
client.task_run.with_streaming_response.create(input="France (2023)", processor="processor").__enter__()
|
|
746
730
|
assert _get_open_connections(self.client) == 0
|
|
747
731
|
|
|
748
732
|
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
|
|
@@ -826,6 +810,55 @@ class TestParallel:
|
|
|
826
810
|
|
|
827
811
|
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
|
|
828
812
|
|
|
813
|
+
def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
814
|
+
# Test that the proxy environment variables are set correctly
|
|
815
|
+
monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
|
|
816
|
+
|
|
817
|
+
client = DefaultHttpxClient()
|
|
818
|
+
|
|
819
|
+
mounts = tuple(client._mounts.items())
|
|
820
|
+
assert len(mounts) == 1
|
|
821
|
+
assert mounts[0][0].pattern == "https://"
|
|
822
|
+
|
|
823
|
+
@pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
|
|
824
|
+
def test_default_client_creation(self) -> None:
|
|
825
|
+
# Ensure that the client can be initialized without any exceptions
|
|
826
|
+
DefaultHttpxClient(
|
|
827
|
+
verify=True,
|
|
828
|
+
cert=None,
|
|
829
|
+
trust_env=True,
|
|
830
|
+
http1=True,
|
|
831
|
+
http2=False,
|
|
832
|
+
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
@pytest.mark.respx(base_url=base_url)
|
|
836
|
+
def test_follow_redirects(self, respx_mock: MockRouter) -> None:
|
|
837
|
+
# Test that the default follow_redirects=True allows following redirects
|
|
838
|
+
respx_mock.post("/redirect").mock(
|
|
839
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
840
|
+
)
|
|
841
|
+
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
|
|
842
|
+
|
|
843
|
+
response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
|
|
844
|
+
assert response.status_code == 200
|
|
845
|
+
assert response.json() == {"status": "ok"}
|
|
846
|
+
|
|
847
|
+
@pytest.mark.respx(base_url=base_url)
|
|
848
|
+
def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
|
|
849
|
+
# Test that follow_redirects=False prevents following redirects
|
|
850
|
+
respx_mock.post("/redirect").mock(
|
|
851
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
with pytest.raises(APIStatusError) as exc_info:
|
|
855
|
+
self.client.post(
|
|
856
|
+
"/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
assert exc_info.value.response.status_code == 302
|
|
860
|
+
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
|
|
861
|
+
|
|
829
862
|
|
|
830
863
|
class TestAsyncParallel:
|
|
831
864
|
client = AsyncParallel(base_url=base_url, api_key=api_key, _strict_response_validation=True)
|
|
@@ -1497,36 +1530,29 @@ class TestAsyncParallel:
|
|
|
1497
1530
|
|
|
1498
1531
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
1499
1532
|
@pytest.mark.respx(base_url=base_url)
|
|
1500
|
-
async def test_retrying_timeout_errors_doesnt_leak(
|
|
1533
|
+
async def test_retrying_timeout_errors_doesnt_leak(
|
|
1534
|
+
self, respx_mock: MockRouter, async_client: AsyncParallel
|
|
1535
|
+
) -> None:
|
|
1501
1536
|
respx_mock.post("/v1/tasks/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
|
|
1502
1537
|
|
|
1503
1538
|
with pytest.raises(APITimeoutError):
|
|
1504
|
-
await
|
|
1505
|
-
"
|
|
1506
|
-
|
|
1507
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
1508
|
-
),
|
|
1509
|
-
cast_to=httpx.Response,
|
|
1510
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
1511
|
-
)
|
|
1539
|
+
await async_client.task_run.with_streaming_response.create(
|
|
1540
|
+
input="France (2023)", processor="processor"
|
|
1541
|
+
).__aenter__()
|
|
1512
1542
|
|
|
1513
1543
|
assert _get_open_connections(self.client) == 0
|
|
1514
1544
|
|
|
1515
1545
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
1516
1546
|
@pytest.mark.respx(base_url=base_url)
|
|
1517
|
-
async def test_retrying_status_errors_doesnt_leak(
|
|
1547
|
+
async def test_retrying_status_errors_doesnt_leak(
|
|
1548
|
+
self, respx_mock: MockRouter, async_client: AsyncParallel
|
|
1549
|
+
) -> None:
|
|
1518
1550
|
respx_mock.post("/v1/tasks/runs").mock(return_value=httpx.Response(500))
|
|
1519
1551
|
|
|
1520
1552
|
with pytest.raises(APIStatusError):
|
|
1521
|
-
await
|
|
1522
|
-
"
|
|
1523
|
-
|
|
1524
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
1525
|
-
),
|
|
1526
|
-
cast_to=httpx.Response,
|
|
1527
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
1528
|
-
)
|
|
1529
|
-
|
|
1553
|
+
await async_client.task_run.with_streaming_response.create(
|
|
1554
|
+
input="France (2023)", processor="processor"
|
|
1555
|
+
).__aenter__()
|
|
1530
1556
|
assert _get_open_connections(self.client) == 0
|
|
1531
1557
|
|
|
1532
1558
|
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
|
|
@@ -1657,3 +1683,52 @@ class TestAsyncParallel:
|
|
|
1657
1683
|
raise AssertionError("calling get_platform using asyncify resulted in a hung process")
|
|
1658
1684
|
|
|
1659
1685
|
time.sleep(0.1)
|
|
1686
|
+
|
|
1687
|
+
async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1688
|
+
# Test that the proxy environment variables are set correctly
|
|
1689
|
+
monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
|
|
1690
|
+
|
|
1691
|
+
client = DefaultAsyncHttpxClient()
|
|
1692
|
+
|
|
1693
|
+
mounts = tuple(client._mounts.items())
|
|
1694
|
+
assert len(mounts) == 1
|
|
1695
|
+
assert mounts[0][0].pattern == "https://"
|
|
1696
|
+
|
|
1697
|
+
@pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
|
|
1698
|
+
async def test_default_client_creation(self) -> None:
|
|
1699
|
+
# Ensure that the client can be initialized without any exceptions
|
|
1700
|
+
DefaultAsyncHttpxClient(
|
|
1701
|
+
verify=True,
|
|
1702
|
+
cert=None,
|
|
1703
|
+
trust_env=True,
|
|
1704
|
+
http1=True,
|
|
1705
|
+
http2=False,
|
|
1706
|
+
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
|
|
1707
|
+
)
|
|
1708
|
+
|
|
1709
|
+
@pytest.mark.respx(base_url=base_url)
|
|
1710
|
+
async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
|
|
1711
|
+
# Test that the default follow_redirects=True allows following redirects
|
|
1712
|
+
respx_mock.post("/redirect").mock(
|
|
1713
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
1714
|
+
)
|
|
1715
|
+
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
|
|
1716
|
+
|
|
1717
|
+
response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
|
|
1718
|
+
assert response.status_code == 200
|
|
1719
|
+
assert response.json() == {"status": "ok"}
|
|
1720
|
+
|
|
1721
|
+
@pytest.mark.respx(base_url=base_url)
|
|
1722
|
+
async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
|
|
1723
|
+
# Test that follow_redirects=False prevents following redirects
|
|
1724
|
+
respx_mock.post("/redirect").mock(
|
|
1725
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
1726
|
+
)
|
|
1727
|
+
|
|
1728
|
+
with pytest.raises(APIStatusError) as exc_info:
|
|
1729
|
+
await self.client.post(
|
|
1730
|
+
"/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
|
|
1731
|
+
)
|
|
1732
|
+
|
|
1733
|
+
assert exc_info.value.response.status_code == 302
|
|
1734
|
+
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
|
|
@@ -21,3 +21,14 @@ def test_recursive_proxy() -> None:
|
|
|
21
21
|
assert dir(proxy) == []
|
|
22
22
|
assert type(proxy).__name__ == "RecursiveLazyProxy"
|
|
23
23
|
assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_isinstance_does_not_error() -> None:
|
|
27
|
+
class AlwaysErrorProxy(LazyProxy[Any]):
|
|
28
|
+
@override
|
|
29
|
+
def __load__(self) -> Any:
|
|
30
|
+
raise RuntimeError("Mocking missing dependency")
|
|
31
|
+
|
|
32
|
+
proxy = AlwaysErrorProxy()
|
|
33
|
+
assert not isinstance(proxy, dict)
|
|
34
|
+
assert isinstance(proxy, LazyProxy)
|
parallel_web-0.1.0/CHANGELOG.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.0 (2025-04-24)
|
|
4
|
-
|
|
5
|
-
Full Changelog: [v0.0.1-alpha.0...v0.1.0](https://github.com/shapleyai/parallel-sdk-python/compare/v0.0.1-alpha.0...v0.1.0)
|
|
6
|
-
|
|
7
|
-
### Features
|
|
8
|
-
|
|
9
|
-
* **api:** add execute method and structured output support ([5e51379](https://github.com/shapleyai/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
10
|
-
* **api:** update via SDK Studio ([c393d04](https://github.com/shapleyai/parallel-sdk-python/commit/c393d048bddb554c37eb750ca57c4335243a70ed))
|
|
11
|
-
* **api:** update via SDK Studio ([6698e71](https://github.com/shapleyai/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
### Chores
|
|
15
|
-
|
|
16
|
-
* go live ([061677a](https://github.com/shapleyai/parallel-sdk-python/commit/061677a22549f3dd3d9f4591c9ccfdf71209c12e))
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import inspect
|
|
4
|
-
from typing import Any
|
|
5
|
-
from typing_extensions import TypeGuard
|
|
6
|
-
|
|
7
|
-
import pydantic
|
|
8
|
-
|
|
9
|
-
from .._compat import PYDANTIC_V2, model_json_schema
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def to_json_schema(
|
|
13
|
-
model: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any],
|
|
14
|
-
) -> dict[str, Any]:
|
|
15
|
-
"""Convert a Pydantic model/type adapter to a JSON schema."""
|
|
16
|
-
if inspect.isclass(model) and is_basemodel_type(model):
|
|
17
|
-
return model_json_schema(model)
|
|
18
|
-
|
|
19
|
-
if PYDANTIC_V2 and isinstance(model, pydantic.TypeAdapter):
|
|
20
|
-
return model.json_schema()
|
|
21
|
-
|
|
22
|
-
raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {model}")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def is_basemodel_type(typ: type | object) -> TypeGuard[type[pydantic.BaseModel]]:
|
|
26
|
-
"""Check if a type is a Pydantic BaseModel."""
|
|
27
|
-
if not inspect.isclass(typ):
|
|
28
|
-
return False
|
|
29
|
-
return issubclass(typ, pydantic.BaseModel)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|