nimble_python 0.16.0__py3-none-any.whl → 0.17.0__py3-none-any.whl

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.
@@ -540,6 +540,10 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
540
540
  files = cast(HttpxRequestFiles, ForceMultipartDict())
541
541
 
542
542
  prepared_url = self._prepare_url(options.url)
543
+ # preserve hard-coded query params from the url
544
+ if params and prepared_url.query:
545
+ params = {**dict(prepared_url.params.items()), **params}
546
+ prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0])
543
547
  if "_" in prepared_url.host:
544
548
  # work around https://github.com/encode/httpx/discussions/2880
545
549
  kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}
nimble_python/_client.py CHANGED
@@ -33,6 +33,7 @@ from ._types import (
33
33
  )
34
34
  from ._utils import (
35
35
  is_given,
36
+ is_mapping_t,
36
37
  maybe_transform,
37
38
  get_async_library,
38
39
  async_maybe_transform,
@@ -72,11 +73,13 @@ __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Nimble", "
72
73
  class Nimble(SyncAPIClient):
73
74
  # client options
74
75
  api_key: str | None
76
+ client_source: str | None
75
77
 
76
78
  def __init__(
77
79
  self,
78
80
  *,
79
81
  api_key: str | None = None,
82
+ client_source: str | None = None,
80
83
  base_url: str | httpx.URL | None = None,
81
84
  timeout: float | Timeout | None | NotGiven = not_given,
82
85
  max_retries: int = DEFAULT_MAX_RETRIES,
@@ -98,17 +101,32 @@ class Nimble(SyncAPIClient):
98
101
  ) -> None:
99
102
  """Construct a new synchronous Nimble client instance.
100
103
 
101
- This automatically infers the `api_key` argument from the `NIMBLE_API_KEY` environment variable if it is not provided.
104
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
105
+ - `api_key` from `NIMBLE_API_KEY`
106
+ - `client_source` from `CLIENT_SOURCE`
102
107
  """
103
108
  if api_key is None:
104
109
  api_key = os.environ.get("NIMBLE_API_KEY")
105
110
  self.api_key = api_key
106
111
 
112
+ if client_source is None:
113
+ client_source = os.environ.get("CLIENT_SOURCE") or "sdk"
114
+ self.client_source = client_source
115
+
107
116
  if base_url is None:
108
117
  base_url = os.environ.get("NIMBLE_BASE_URL")
109
118
  if base_url is None:
110
119
  base_url = f"https://sdk.nimbleway.com"
111
120
 
121
+ custom_headers_env = os.environ.get("NIMBLE_CUSTOM_HEADERS")
122
+ if custom_headers_env is not None:
123
+ parsed: dict[str, str] = {}
124
+ for line in custom_headers_env.split("\n"):
125
+ colon = line.find(":")
126
+ if colon >= 0:
127
+ parsed[line[:colon].strip()] = line[colon + 1 :].strip()
128
+ default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
129
+
112
130
  super().__init__(
113
131
  version=__version__,
114
132
  base_url=base_url,
@@ -171,6 +189,7 @@ class Nimble(SyncAPIClient):
171
189
  return {
172
190
  **super().default_headers,
173
191
  "X-Stainless-Async": "false",
192
+ "X-Client-Source": self.client_source if self.client_source is not None else Omit(),
174
193
  **self._custom_headers,
175
194
  }
176
195
 
@@ -187,6 +206,7 @@ class Nimble(SyncAPIClient):
187
206
  self,
188
207
  *,
189
208
  api_key: str | None = None,
209
+ client_source: str | None = None,
190
210
  base_url: str | httpx.URL | None = None,
191
211
  timeout: float | Timeout | None | NotGiven = not_given,
192
212
  http_client: httpx.Client | None = None,
@@ -221,6 +241,7 @@ class Nimble(SyncAPIClient):
221
241
  http_client = http_client or self._client
222
242
  return self.__class__(
223
243
  api_key=api_key or self.api_key,
244
+ client_source=client_source or self.client_source,
224
245
  base_url=base_url or self.base_url,
225
246
  timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
226
247
  http_client=http_client,
@@ -498,9 +519,9 @@ class Nimble(SyncAPIClient):
498
519
  ]
499
520
  | Omit = omit,
500
521
  device: Literal["desktop", "mobile", "tablet"] | Omit = omit,
501
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"] | Omit = omit,
522
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"] | Omit = omit,
502
523
  expected_status_codes: Iterable[int] | Omit = omit,
503
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
524
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
504
525
  headers: Dict[str, Union[str, SequenceNotStr[str], None]] | Omit = omit,
505
526
  http2: bool | Omit = omit,
506
527
  is_xhr: bool | Omit = omit,
@@ -1040,6 +1061,7 @@ class Nimble(SyncAPIClient):
1040
1061
  "auto",
1041
1062
  ]
1042
1063
  | Omit = omit,
1064
+ markdown_backend: Literal["full_page", "main_content"] | Omit = omit,
1043
1065
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] | Omit = omit,
1044
1066
  network_capture: Iterable[client_extract_params.NetworkCapture] | Omit = omit,
1045
1067
  os: Literal["windows", "mac os", "linux", "android", "ios"] | Omit = omit,
@@ -1154,6 +1176,10 @@ class Nimble(SyncAPIClient):
1154
1176
 
1155
1177
  locale: Locale for browser language and region settings
1156
1178
 
1179
+ markdown_backend: Selects which markdown conversion strategy to use. "full_page" converts the
1180
+ entire HTML page. "main_content" uses Mozilla Readability to extract the main
1181
+ article content before converting.
1182
+
1157
1183
  method: HTTP method for the request
1158
1184
 
1159
1185
  network_capture: Filters for capturing network traffic
@@ -1203,6 +1229,7 @@ class Nimble(SyncAPIClient):
1203
1229
  "http2": http2,
1204
1230
  "is_xhr": is_xhr,
1205
1231
  "locale": locale,
1232
+ "markdown_backend": markdown_backend,
1206
1233
  "method": method,
1207
1234
  "network_capture": network_capture,
1208
1235
  "os": os,
@@ -1489,9 +1516,9 @@ class Nimble(SyncAPIClient):
1489
1516
  ]
1490
1517
  | Omit = omit,
1491
1518
  device: Literal["desktop", "mobile", "tablet"] | Omit = omit,
1492
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"] | Omit = omit,
1519
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"] | Omit = omit,
1493
1520
  expected_status_codes: Iterable[int] | Omit = omit,
1494
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
1521
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
1495
1522
  headers: Dict[str, Union[str, SequenceNotStr[str], None]] | Omit = omit,
1496
1523
  http2: bool | Omit = omit,
1497
1524
  is_xhr: bool | Omit = omit,
@@ -2031,6 +2058,7 @@ class Nimble(SyncAPIClient):
2031
2058
  "auto",
2032
2059
  ]
2033
2060
  | Omit = omit,
2061
+ markdown_backend: Literal["full_page", "main_content"] | Omit = omit,
2034
2062
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] | Omit = omit,
2035
2063
  network_capture: Iterable[client_extract_async_params.NetworkCapture] | Omit = omit,
2036
2064
  os: Literal["windows", "mac os", "linux", "android", "ios"] | Omit = omit,
@@ -2151,6 +2179,10 @@ class Nimble(SyncAPIClient):
2151
2179
 
2152
2180
  locale: Locale for browser language and region settings
2153
2181
 
2182
+ markdown_backend: Selects which markdown conversion strategy to use. "full_page" converts the
2183
+ entire HTML page. "main_content" uses Mozilla Readability to extract the main
2184
+ article content before converting.
2185
+
2154
2186
  method: HTTP method for the request
2155
2187
 
2156
2188
  network_capture: Filters for capturing network traffic
@@ -2209,6 +2241,7 @@ class Nimble(SyncAPIClient):
2209
2241
  "http2": http2,
2210
2242
  "is_xhr": is_xhr,
2211
2243
  "locale": locale,
2244
+ "markdown_backend": markdown_backend,
2212
2245
  "method": method,
2213
2246
  "network_capture": network_capture,
2214
2247
  "os": os,
@@ -3272,11 +3305,13 @@ class Nimble(SyncAPIClient):
3272
3305
  class AsyncNimble(AsyncAPIClient):
3273
3306
  # client options
3274
3307
  api_key: str | None
3308
+ client_source: str | None
3275
3309
 
3276
3310
  def __init__(
3277
3311
  self,
3278
3312
  *,
3279
3313
  api_key: str | None = None,
3314
+ client_source: str | None = None,
3280
3315
  base_url: str | httpx.URL | None = None,
3281
3316
  timeout: float | Timeout | None | NotGiven = not_given,
3282
3317
  max_retries: int = DEFAULT_MAX_RETRIES,
@@ -3298,17 +3333,32 @@ class AsyncNimble(AsyncAPIClient):
3298
3333
  ) -> None:
3299
3334
  """Construct a new async AsyncNimble client instance.
3300
3335
 
3301
- This automatically infers the `api_key` argument from the `NIMBLE_API_KEY` environment variable if it is not provided.
3336
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
3337
+ - `api_key` from `NIMBLE_API_KEY`
3338
+ - `client_source` from `CLIENT_SOURCE`
3302
3339
  """
3303
3340
  if api_key is None:
3304
3341
  api_key = os.environ.get("NIMBLE_API_KEY")
3305
3342
  self.api_key = api_key
3306
3343
 
3344
+ if client_source is None:
3345
+ client_source = os.environ.get("CLIENT_SOURCE") or "sdk"
3346
+ self.client_source = client_source
3347
+
3307
3348
  if base_url is None:
3308
3349
  base_url = os.environ.get("NIMBLE_BASE_URL")
3309
3350
  if base_url is None:
3310
3351
  base_url = f"https://sdk.nimbleway.com"
3311
3352
 
3353
+ custom_headers_env = os.environ.get("NIMBLE_CUSTOM_HEADERS")
3354
+ if custom_headers_env is not None:
3355
+ parsed: dict[str, str] = {}
3356
+ for line in custom_headers_env.split("\n"):
3357
+ colon = line.find(":")
3358
+ if colon >= 0:
3359
+ parsed[line[:colon].strip()] = line[colon + 1 :].strip()
3360
+ default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
3361
+
3312
3362
  super().__init__(
3313
3363
  version=__version__,
3314
3364
  base_url=base_url,
@@ -3371,6 +3421,7 @@ class AsyncNimble(AsyncAPIClient):
3371
3421
  return {
3372
3422
  **super().default_headers,
3373
3423
  "X-Stainless-Async": f"async:{get_async_library()}",
3424
+ "X-Client-Source": self.client_source if self.client_source is not None else Omit(),
3374
3425
  **self._custom_headers,
3375
3426
  }
3376
3427
 
@@ -3387,6 +3438,7 @@ class AsyncNimble(AsyncAPIClient):
3387
3438
  self,
3388
3439
  *,
3389
3440
  api_key: str | None = None,
3441
+ client_source: str | None = None,
3390
3442
  base_url: str | httpx.URL | None = None,
3391
3443
  timeout: float | Timeout | None | NotGiven = not_given,
3392
3444
  http_client: httpx.AsyncClient | None = None,
@@ -3421,6 +3473,7 @@ class AsyncNimble(AsyncAPIClient):
3421
3473
  http_client = http_client or self._client
3422
3474
  return self.__class__(
3423
3475
  api_key=api_key or self.api_key,
3476
+ client_source=client_source or self.client_source,
3424
3477
  base_url=base_url or self.base_url,
3425
3478
  timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
3426
3479
  http_client=http_client,
@@ -3698,9 +3751,9 @@ class AsyncNimble(AsyncAPIClient):
3698
3751
  ]
3699
3752
  | Omit = omit,
3700
3753
  device: Literal["desktop", "mobile", "tablet"] | Omit = omit,
3701
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"] | Omit = omit,
3754
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"] | Omit = omit,
3702
3755
  expected_status_codes: Iterable[int] | Omit = omit,
3703
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
3756
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
3704
3757
  headers: Dict[str, Union[str, SequenceNotStr[str], None]] | Omit = omit,
3705
3758
  http2: bool | Omit = omit,
3706
3759
  is_xhr: bool | Omit = omit,
@@ -4240,6 +4293,7 @@ class AsyncNimble(AsyncAPIClient):
4240
4293
  "auto",
4241
4294
  ]
4242
4295
  | Omit = omit,
4296
+ markdown_backend: Literal["full_page", "main_content"] | Omit = omit,
4243
4297
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] | Omit = omit,
4244
4298
  network_capture: Iterable[client_extract_params.NetworkCapture] | Omit = omit,
4245
4299
  os: Literal["windows", "mac os", "linux", "android", "ios"] | Omit = omit,
@@ -4354,6 +4408,10 @@ class AsyncNimble(AsyncAPIClient):
4354
4408
 
4355
4409
  locale: Locale for browser language and region settings
4356
4410
 
4411
+ markdown_backend: Selects which markdown conversion strategy to use. "full_page" converts the
4412
+ entire HTML page. "main_content" uses Mozilla Readability to extract the main
4413
+ article content before converting.
4414
+
4357
4415
  method: HTTP method for the request
4358
4416
 
4359
4417
  network_capture: Filters for capturing network traffic
@@ -4403,6 +4461,7 @@ class AsyncNimble(AsyncAPIClient):
4403
4461
  "http2": http2,
4404
4462
  "is_xhr": is_xhr,
4405
4463
  "locale": locale,
4464
+ "markdown_backend": markdown_backend,
4406
4465
  "method": method,
4407
4466
  "network_capture": network_capture,
4408
4467
  "os": os,
@@ -4689,9 +4748,9 @@ class AsyncNimble(AsyncAPIClient):
4689
4748
  ]
4690
4749
  | Omit = omit,
4691
4750
  device: Literal["desktop", "mobile", "tablet"] | Omit = omit,
4692
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"] | Omit = omit,
4751
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"] | Omit = omit,
4693
4752
  expected_status_codes: Iterable[int] | Omit = omit,
4694
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
4753
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
4695
4754
  headers: Dict[str, Union[str, SequenceNotStr[str], None]] | Omit = omit,
4696
4755
  http2: bool | Omit = omit,
4697
4756
  is_xhr: bool | Omit = omit,
@@ -5231,6 +5290,7 @@ class AsyncNimble(AsyncAPIClient):
5231
5290
  "auto",
5232
5291
  ]
5233
5292
  | Omit = omit,
5293
+ markdown_backend: Literal["full_page", "main_content"] | Omit = omit,
5234
5294
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] | Omit = omit,
5235
5295
  network_capture: Iterable[client_extract_async_params.NetworkCapture] | Omit = omit,
5236
5296
  os: Literal["windows", "mac os", "linux", "android", "ios"] | Omit = omit,
@@ -5351,6 +5411,10 @@ class AsyncNimble(AsyncAPIClient):
5351
5411
 
5352
5412
  locale: Locale for browser language and region settings
5353
5413
 
5414
+ markdown_backend: Selects which markdown conversion strategy to use. "full_page" converts the
5415
+ entire HTML page. "main_content" uses Mozilla Readability to extract the main
5416
+ article content before converting.
5417
+
5354
5418
  method: HTTP method for the request
5355
5419
 
5356
5420
  network_capture: Filters for capturing network traffic
@@ -5409,6 +5473,7 @@ class AsyncNimble(AsyncAPIClient):
5409
5473
  "http2": http2,
5410
5474
  "is_xhr": is_xhr,
5411
5475
  "locale": locale,
5476
+ "markdown_backend": markdown_backend,
5412
5477
  "method": method,
5413
5478
  "network_capture": network_capture,
5414
5479
  "os": os,
nimble_python/_files.py CHANGED
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  import io
4
4
  import os
5
5
  import pathlib
6
- from typing import overload
7
- from typing_extensions import TypeGuard
6
+ from typing import Sequence, cast, overload
7
+ from typing_extensions import TypeVar, TypeGuard
8
8
 
9
9
  import anyio
10
10
 
@@ -17,7 +17,9 @@ from ._types import (
17
17
  HttpxFileContent,
18
18
  HttpxRequestFiles,
19
19
  )
20
- from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
20
+ from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t
21
+
22
+ _T = TypeVar("_T")
21
23
 
22
24
 
23
25
  def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
@@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent:
121
123
  return await anyio.Path(file).read_bytes()
122
124
 
123
125
  return file
126
+
127
+
128
+ def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T:
129
+ """Copy only the containers along the given paths.
130
+
131
+ Used to guard against mutation by extract_files without copying the entire structure.
132
+ Only dicts and lists that lie on a path are copied; everything else
133
+ is returned by reference.
134
+
135
+ For example, given paths=[["foo", "files", "file"]] and the structure:
136
+ {
137
+ "foo": {
138
+ "bar": {"baz": {}},
139
+ "files": {"file": <content>}
140
+ }
141
+ }
142
+ The root dict, "foo", and "files" are copied (they lie on the path).
143
+ "bar" and "baz" are returned by reference (off the path).
144
+ """
145
+ return _deepcopy_with_paths(item, paths, 0)
146
+
147
+
148
+ def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T:
149
+ if not paths:
150
+ return item
151
+ if is_mapping(item):
152
+ key_to_paths: dict[str, list[Sequence[str]]] = {}
153
+ for path in paths:
154
+ if index < len(path):
155
+ key_to_paths.setdefault(path[index], []).append(path)
156
+
157
+ # if no path continues through this mapping, it won't be mutated and copying it is redundant
158
+ if not key_to_paths:
159
+ return item
160
+
161
+ result = dict(item)
162
+ for key, subpaths in key_to_paths.items():
163
+ if key in result:
164
+ result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1)
165
+ return cast(_T, result)
166
+ if is_list(item):
167
+ array_paths = [path for path in paths if index < len(path) and path[index] == "<array>"]
168
+
169
+ # if no path expects a list here, nothing will be mutated inside it - return by reference
170
+ if not array_paths:
171
+ return cast(_T, item)
172
+ return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item])
173
+ return item
nimble_python/_qs.py CHANGED
@@ -2,17 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any, List, Tuple, Union, Mapping, TypeVar
4
4
  from urllib.parse import parse_qs, urlencode
5
- from typing_extensions import Literal, get_args
5
+ from typing_extensions import get_args
6
6
 
7
- from ._types import NotGiven, not_given
7
+ from ._types import NotGiven, ArrayFormat, NestedFormat, not_given
8
8
  from ._utils import flatten
9
9
 
10
10
  _T = TypeVar("_T")
11
11
 
12
-
13
- ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
14
- NestedFormat = Literal["dots", "brackets"]
15
-
16
12
  PrimitiveData = Union[str, int, float, bool, None]
17
13
  # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"]
18
14
  # https://github.com/microsoft/pyright/issues/3555
nimble_python/_types.py CHANGED
@@ -47,6 +47,9 @@ AnyMapping = Mapping[str, object]
47
47
  ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
48
48
  _T = TypeVar("_T")
49
49
 
50
+ ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
51
+ NestedFormat = Literal["dots", "brackets"]
52
+
50
53
 
51
54
  # Approximates httpx internal ProxiesTypes and RequestFiles types
52
55
  # while adding support for `PathLike` instances
@@ -24,7 +24,6 @@ from ._utils import (
24
24
  coerce_integer as coerce_integer,
25
25
  file_from_path as file_from_path,
26
26
  strip_not_given as strip_not_given,
27
- deepcopy_minimal as deepcopy_minimal,
28
27
  get_async_library as get_async_library,
29
28
  maybe_coerce_float as maybe_coerce_float,
30
29
  get_required_header as get_required_header,
@@ -17,11 +17,11 @@ from typing import (
17
17
  )
18
18
  from pathlib import Path
19
19
  from datetime import date, datetime
20
- from typing_extensions import TypeGuard
20
+ from typing_extensions import TypeGuard, get_args
21
21
 
22
22
  import sniffio
23
23
 
24
- from .._types import Omit, NotGiven, FileTypes, HeadersLike
24
+ from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike
25
25
 
26
26
  _T = TypeVar("_T")
27
27
  _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
@@ -40,25 +40,45 @@ def extract_files(
40
40
  query: Mapping[str, object],
41
41
  *,
42
42
  paths: Sequence[Sequence[str]],
43
+ array_format: ArrayFormat = "brackets",
43
44
  ) -> list[tuple[str, FileTypes]]:
44
45
  """Recursively extract files from the given dictionary based on specified paths.
45
46
 
46
47
  A path may look like this ['foo', 'files', '<array>', 'data'].
47
48
 
49
+ ``array_format`` controls how ``<array>`` segments contribute to the emitted
50
+ field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and
51
+ ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``).
52
+
48
53
  Note: this mutates the given dictionary.
49
54
  """
50
55
  files: list[tuple[str, FileTypes]] = []
51
56
  for path in paths:
52
- files.extend(_extract_items(query, path, index=0, flattened_key=None))
57
+ files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format))
53
58
  return files
54
59
 
55
60
 
61
+ def _array_suffix(array_format: ArrayFormat, array_index: int) -> str:
62
+ if array_format == "brackets":
63
+ return "[]"
64
+ if array_format == "indices":
65
+ return f"[{array_index}]"
66
+ if array_format == "repeat" or array_format == "comma":
67
+ # Both repeat the bare field name for each file part; there is no
68
+ # meaningful way to comma-join binary parts.
69
+ return ""
70
+ raise NotImplementedError(
71
+ f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}"
72
+ )
73
+
74
+
56
75
  def _extract_items(
57
76
  obj: object,
58
77
  path: Sequence[str],
59
78
  *,
60
79
  index: int,
61
80
  flattened_key: str | None,
81
+ array_format: ArrayFormat,
62
82
  ) -> list[tuple[str, FileTypes]]:
63
83
  try:
64
84
  key = path[index]
@@ -75,9 +95,11 @@ def _extract_items(
75
95
 
76
96
  if is_list(obj):
77
97
  files: list[tuple[str, FileTypes]] = []
78
- for entry in obj:
79
- assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
80
- files.append((flattened_key + "[]", cast(FileTypes, entry)))
98
+ for array_index, entry in enumerate(obj):
99
+ suffix = _array_suffix(array_format, array_index)
100
+ emitted_key = (flattened_key + suffix) if flattened_key else suffix
101
+ assert_is_file_content(entry, key=emitted_key)
102
+ files.append((emitted_key, cast(FileTypes, entry)))
81
103
  return files
82
104
 
83
105
  assert_is_file_content(obj, key=flattened_key)
@@ -86,8 +108,9 @@ def _extract_items(
86
108
  index += 1
87
109
  if is_dict(obj):
88
110
  try:
89
- # We are at the last entry in the path so we must remove the field
90
- if (len(path)) == index:
111
+ # Remove the field if there are no more dict keys in the path,
112
+ # only "<array>" traversal markers or end.
113
+ if all(p == "<array>" for p in path[index:]):
91
114
  item = obj.pop(key)
92
115
  else:
93
116
  item = obj[key]
@@ -105,6 +128,7 @@ def _extract_items(
105
128
  path,
106
129
  index=index,
107
130
  flattened_key=flattened_key,
131
+ array_format=array_format,
108
132
  )
109
133
  elif is_list(obj):
110
134
  if key != "<array>":
@@ -116,9 +140,12 @@ def _extract_items(
116
140
  item,
117
141
  path,
118
142
  index=index,
119
- flattened_key=flattened_key + "[]" if flattened_key is not None else "[]",
143
+ flattened_key=(
144
+ (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index)
145
+ ),
146
+ array_format=array_format,
120
147
  )
121
- for item in obj
148
+ for array_index, item in enumerate(obj)
122
149
  ]
123
150
  )
124
151
 
@@ -176,21 +203,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]:
176
203
  return isinstance(obj, Iterable)
177
204
 
178
205
 
179
- def deepcopy_minimal(item: _T) -> _T:
180
- """Minimal reimplementation of copy.deepcopy() that will only copy certain object types:
181
-
182
- - mappings, e.g. `dict`
183
- - list
184
-
185
- This is done for performance reasons.
186
- """
187
- if is_mapping(item):
188
- return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()})
189
- if is_list(item):
190
- return cast(_T, [deepcopy_minimal(entry) for entry in item])
191
- return item
192
-
193
-
194
206
  # copied from https://github.com/Rapptz/RoboDanny
195
207
  def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str:
196
208
  size = len(seq)
nimble_python/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "nimble_python"
4
- __version__ = "0.16.0" # x-release-please-version
4
+ __version__ = "0.17.0" # x-release-please-version
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import typing_extensions
5
6
  from typing import Dict, List, Iterable, Optional
6
7
  from typing_extensions import Literal, overload
7
8
 
@@ -120,11 +121,11 @@ class AgentResource(SyncAPIResource):
120
121
  def generate(
121
122
  self,
122
123
  *,
123
- agent_name: str,
124
124
  prompt: str,
125
125
  url: str,
126
+ agent_name: Optional[str] | Omit = omit,
126
127
  input_schema: object | Omit = omit,
127
- metadata: Optional[object] | Omit = omit,
128
+ metadata: Optional[agent_generate_params.CreateAgentGenerationRequestMetadata] | Omit = omit,
128
129
  output_schema: object | Omit = omit,
129
130
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
130
131
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -174,15 +175,15 @@ class AgentResource(SyncAPIResource):
174
175
  """
175
176
  ...
176
177
 
177
- @required_args(["agent_name", "prompt", "url"], ["from_agent", "prompt"])
178
+ @required_args(["prompt", "url"], ["from_agent", "prompt"])
178
179
  def generate(
179
180
  self,
180
181
  *,
181
- agent_name: str | Omit = omit,
182
182
  prompt: str,
183
183
  url: str | Omit = omit,
184
+ agent_name: Optional[str] | Omit = omit,
184
185
  input_schema: object | Omit = omit,
185
- metadata: Optional[object] | Omit = omit,
186
+ metadata: Optional[agent_generate_params.CreateAgentGenerationRequestMetadata] | Omit = omit,
186
187
  output_schema: object | Omit = omit,
187
188
  from_agent: str | Omit = omit,
188
189
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -196,9 +197,9 @@ class AgentResource(SyncAPIResource):
196
197
  "/v1/agents/generations",
197
198
  body=maybe_transform(
198
199
  {
199
- "agent_name": agent_name,
200
200
  "prompt": prompt,
201
201
  "url": url,
202
+ "agent_name": agent_name,
202
203
  "input_schema": input_schema,
203
204
  "metadata": metadata,
204
205
  "output_schema": output_schema,
@@ -278,6 +279,7 @@ class AgentResource(SyncAPIResource):
278
279
  cast_to=AgentGetGenerationResponse,
279
280
  )
280
281
 
282
+ @typing_extensions.deprecated("deprecated")
281
283
  def publish(
282
284
  self,
283
285
  agent_name: str,
@@ -318,7 +320,7 @@ class AgentResource(SyncAPIResource):
318
320
  *,
319
321
  agent: str,
320
322
  params: Dict[str, object],
321
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
323
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
322
324
  localization: bool | Omit = omit,
323
325
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
324
326
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -365,7 +367,7 @@ class AgentResource(SyncAPIResource):
365
367
  agent: str,
366
368
  params: Dict[str, object],
367
369
  callback_url: str | Omit = omit,
368
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
370
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
369
371
  localization: bool | Omit = omit,
370
372
  storage_compress: bool | Omit = omit,
371
373
  storage_object_name: str | Omit = omit,
@@ -546,11 +548,11 @@ class AsyncAgentResource(AsyncAPIResource):
546
548
  async def generate(
547
549
  self,
548
550
  *,
549
- agent_name: str,
550
551
  prompt: str,
551
552
  url: str,
553
+ agent_name: Optional[str] | Omit = omit,
552
554
  input_schema: object | Omit = omit,
553
- metadata: Optional[object] | Omit = omit,
555
+ metadata: Optional[agent_generate_params.CreateAgentGenerationRequestMetadata] | Omit = omit,
554
556
  output_schema: object | Omit = omit,
555
557
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
556
558
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -600,15 +602,15 @@ class AsyncAgentResource(AsyncAPIResource):
600
602
  """
601
603
  ...
602
604
 
603
- @required_args(["agent_name", "prompt", "url"], ["from_agent", "prompt"])
605
+ @required_args(["prompt", "url"], ["from_agent", "prompt"])
604
606
  async def generate(
605
607
  self,
606
608
  *,
607
- agent_name: str | Omit = omit,
608
609
  prompt: str,
609
610
  url: str | Omit = omit,
611
+ agent_name: Optional[str] | Omit = omit,
610
612
  input_schema: object | Omit = omit,
611
- metadata: Optional[object] | Omit = omit,
613
+ metadata: Optional[agent_generate_params.CreateAgentGenerationRequestMetadata] | Omit = omit,
612
614
  output_schema: object | Omit = omit,
613
615
  from_agent: str | Omit = omit,
614
616
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -622,9 +624,9 @@ class AsyncAgentResource(AsyncAPIResource):
622
624
  "/v1/agents/generations",
623
625
  body=await async_maybe_transform(
624
626
  {
625
- "agent_name": agent_name,
626
627
  "prompt": prompt,
627
628
  "url": url,
629
+ "agent_name": agent_name,
628
630
  "input_schema": input_schema,
629
631
  "metadata": metadata,
630
632
  "output_schema": output_schema,
@@ -704,6 +706,7 @@ class AsyncAgentResource(AsyncAPIResource):
704
706
  cast_to=AgentGetGenerationResponse,
705
707
  )
706
708
 
709
+ @typing_extensions.deprecated("deprecated")
707
710
  async def publish(
708
711
  self,
709
712
  agent_name: str,
@@ -744,7 +747,7 @@ class AsyncAgentResource(AsyncAPIResource):
744
747
  *,
745
748
  agent: str,
746
749
  params: Dict[str, object],
747
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
750
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
748
751
  localization: bool | Omit = omit,
749
752
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
750
753
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -791,7 +794,7 @@ class AsyncAgentResource(AsyncAPIResource):
791
794
  agent: str,
792
795
  params: Dict[str, object],
793
796
  callback_url: str | Omit = omit,
794
- formats: List[Literal["html", "markdown", "screenshot", "headers"]] | Omit = omit,
797
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]] | Omit = omit,
795
798
  localization: bool | Omit = omit,
796
799
  storage_compress: bool | Omit = omit,
797
800
  storage_object_name: str | Omit = omit,
@@ -906,8 +909,10 @@ class AgentResourceWithRawResponse:
906
909
  self.get_generation = to_raw_response_wrapper(
907
910
  agent.get_generation,
908
911
  )
909
- self.publish = to_raw_response_wrapper(
910
- agent.publish,
912
+ self.publish = ( # pyright: ignore[reportDeprecated]
913
+ to_raw_response_wrapper(
914
+ agent.publish, # pyright: ignore[reportDeprecated],
915
+ )
911
916
  )
912
917
  self.run = to_raw_response_wrapper(
913
918
  agent.run,
@@ -936,8 +941,10 @@ class AsyncAgentResourceWithRawResponse:
936
941
  self.get_generation = async_to_raw_response_wrapper(
937
942
  agent.get_generation,
938
943
  )
939
- self.publish = async_to_raw_response_wrapper(
940
- agent.publish,
944
+ self.publish = ( # pyright: ignore[reportDeprecated]
945
+ async_to_raw_response_wrapper(
946
+ agent.publish, # pyright: ignore[reportDeprecated],
947
+ )
941
948
  )
942
949
  self.run = async_to_raw_response_wrapper(
943
950
  agent.run,
@@ -966,8 +973,10 @@ class AgentResourceWithStreamingResponse:
966
973
  self.get_generation = to_streamed_response_wrapper(
967
974
  agent.get_generation,
968
975
  )
969
- self.publish = to_streamed_response_wrapper(
970
- agent.publish,
976
+ self.publish = ( # pyright: ignore[reportDeprecated]
977
+ to_streamed_response_wrapper(
978
+ agent.publish, # pyright: ignore[reportDeprecated],
979
+ )
971
980
  )
972
981
  self.run = to_streamed_response_wrapper(
973
982
  agent.run,
@@ -996,8 +1005,10 @@ class AsyncAgentResourceWithStreamingResponse:
996
1005
  self.get_generation = async_to_streamed_response_wrapper(
997
1006
  agent.get_generation,
998
1007
  )
999
- self.publish = async_to_streamed_response_wrapper(
1000
- agent.publish,
1008
+ self.publish = ( # pyright: ignore[reportDeprecated]
1009
+ async_to_streamed_response_wrapper(
1010
+ agent.publish, # pyright: ignore[reportDeprecated],
1011
+ )
1001
1012
  )
1002
1013
  self.run = async_to_streamed_response_wrapper(
1003
1014
  agent.run,
@@ -5,23 +5,38 @@ from __future__ import annotations
5
5
  from typing import Union, Optional
6
6
  from typing_extensions import Required, TypeAlias, TypedDict
7
7
 
8
- __all__ = ["AgentGenerateParams", "CreateAgentGenerationRequest", "CreateAgentRefinementRequest"]
8
+ from .._types import SequenceNotStr
9
9
 
10
+ __all__ = [
11
+ "AgentGenerateParams",
12
+ "CreateAgentGenerationRequest",
13
+ "CreateAgentGenerationRequestMetadata",
14
+ "CreateAgentRefinementRequest",
15
+ ]
10
16
 
11
- class CreateAgentGenerationRequest(TypedDict, total=False):
12
- agent_name: Required[str]
13
17
 
18
+ class CreateAgentGenerationRequest(TypedDict, total=False):
14
19
  prompt: Required[str]
15
20
 
16
21
  url: Required[str]
17
22
 
23
+ agent_name: Optional[str]
24
+
18
25
  input_schema: object
19
26
 
20
- metadata: Optional[object]
27
+ metadata: Optional[CreateAgentGenerationRequestMetadata]
21
28
 
22
29
  output_schema: object
23
30
 
24
31
 
32
+ class CreateAgentGenerationRequestMetadata(TypedDict, total=False):
33
+ description: Optional[str]
34
+
35
+ display_name: Optional[str]
36
+
37
+ tags: SequenceNotStr[str]
38
+
39
+
25
40
  class CreateAgentRefinementRequest(TypedDict, total=False):
26
41
  from_agent: Required[str]
27
42
 
@@ -1,11 +1,47 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
- from typing import Optional
3
+ from typing import List, Optional
4
4
  from datetime import datetime
5
5
 
6
6
  from .._models import BaseModel
7
7
 
8
- __all__ = ["AgentGenerateResponse"]
8
+ __all__ = ["AgentGenerateResponse", "GeneratedVersion", "GeneratedVersionMetadata"]
9
+
10
+
11
+ class GeneratedVersionMetadata(BaseModel):
12
+ data_source: Optional[str] = None
13
+
14
+ description: Optional[str] = None
15
+
16
+ display_name: Optional[str] = None
17
+
18
+ domain: Optional[str] = None
19
+
20
+ entity_type: Optional[str] = None
21
+
22
+ tags: Optional[List[str]] = None
23
+
24
+ vertical: Optional[str] = None
25
+
26
+
27
+ class GeneratedVersion(BaseModel):
28
+ id: str
29
+
30
+ agent_name: str
31
+
32
+ created_at: datetime
33
+
34
+ input_schema: object
35
+
36
+ metadata: GeneratedVersionMetadata
37
+
38
+ output_schema: object
39
+
40
+ steps: List[object]
41
+
42
+ version_number: int
43
+
44
+ output_sample_data: Optional[object] = None
9
45
 
10
46
 
11
47
  class AgentGenerateResponse(BaseModel):
@@ -21,7 +57,7 @@ class AgentGenerateResponse(BaseModel):
21
57
 
22
58
  error: Optional[str] = None
23
59
 
24
- generated_version: Optional[object] = None
60
+ generated_version: Optional[GeneratedVersion] = None
25
61
 
26
62
  generated_version_id: Optional[str] = None
27
63
 
@@ -1,11 +1,47 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
- from typing import Optional
3
+ from typing import List, Optional
4
4
  from datetime import datetime
5
5
 
6
6
  from .._models import BaseModel
7
7
 
8
- __all__ = ["AgentGetGenerationResponse"]
8
+ __all__ = ["AgentGetGenerationResponse", "GeneratedVersion", "GeneratedVersionMetadata"]
9
+
10
+
11
+ class GeneratedVersionMetadata(BaseModel):
12
+ data_source: Optional[str] = None
13
+
14
+ description: Optional[str] = None
15
+
16
+ display_name: Optional[str] = None
17
+
18
+ domain: Optional[str] = None
19
+
20
+ entity_type: Optional[str] = None
21
+
22
+ tags: Optional[List[str]] = None
23
+
24
+ vertical: Optional[str] = None
25
+
26
+
27
+ class GeneratedVersion(BaseModel):
28
+ id: str
29
+
30
+ agent_name: str
31
+
32
+ created_at: datetime
33
+
34
+ input_schema: object
35
+
36
+ metadata: GeneratedVersionMetadata
37
+
38
+ output_schema: object
39
+
40
+ steps: List[object]
41
+
42
+ version_number: int
43
+
44
+ output_sample_data: Optional[object] = None
9
45
 
10
46
 
11
47
  class AgentGetGenerationResponse(BaseModel):
@@ -21,7 +57,7 @@ class AgentGetGenerationResponse(BaseModel):
21
57
 
22
58
  error: Optional[str] = None
23
59
 
24
- generated_version: Optional[object] = None
60
+ generated_version: Optional[GeneratedVersion] = None
25
61
 
26
62
  generated_version_id: Optional[str] = None
27
63
 
@@ -1,6 +1,6 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
- from typing import List, Optional
3
+ from typing import List, Union, Optional
4
4
 
5
5
  from .._models import BaseModel
6
6
 
@@ -14,14 +14,16 @@ class FeatureFlags(BaseModel):
14
14
 
15
15
 
16
16
  class InputProperty(BaseModel):
17
- default: Optional[str] = None
17
+ default: Union[str, bool, float, None] = None
18
18
 
19
19
  description: Optional[str] = None
20
20
 
21
- examples: Optional[List[str]] = None
21
+ examples: Optional[List[object]] = None
22
22
 
23
23
  is_localization_param: Optional[bool] = None
24
24
 
25
+ is_pagination_param: Optional[bool] = None
26
+
25
27
  name: Optional[str] = None
26
28
 
27
29
  required: Optional[bool] = None
@@ -16,7 +16,7 @@ class AgentRunAsyncParams(TypedDict, total=False):
16
16
  callback_url: str
17
17
  """URL to call back when async operation completes"""
18
18
 
19
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
19
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
20
20
  """Response formats to include. All disabled by default."""
21
21
 
22
22
  localization: bool
@@ -15,7 +15,7 @@ class AgentRunBatchParams(TypedDict, total=False):
15
15
 
16
16
 
17
17
  class Input(TypedDict, total=False):
18
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
18
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
19
19
  """Response formats to include. All disabled by default."""
20
20
 
21
21
  localization: bool
@@ -26,7 +26,7 @@ class Input(TypedDict, total=False):
26
26
  class SharedInputs(TypedDict, total=False):
27
27
  agent: Required[str]
28
28
 
29
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
29
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
30
30
  """Response formats to include. All disabled by default."""
31
31
 
32
32
  localization: bool
@@ -13,7 +13,7 @@ class AgentRunParams(TypedDict, total=False):
13
13
 
14
14
  params: Required[Dict[str, object]]
15
15
 
16
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
16
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
17
17
  """Response formats to include. All disabled by default."""
18
18
 
19
19
  localization: bool
@@ -240,6 +240,9 @@ class Data(BaseModel):
240
240
  html: Optional[str] = None
241
241
  """The HTML content of the page."""
242
242
 
243
+ links: Optional[List[str]] = None
244
+ """List of all unique URLs found on the page."""
245
+
243
246
  markdown: Optional[str] = None
244
247
  """The Markdown version of the HTML content."""
245
248
 
@@ -313,13 +313,13 @@ class ClientExtractAsyncParams(TypedDict, total=False):
313
313
  device: Literal["desktop", "mobile", "tablet"]
314
314
  """Device type for browser emulation"""
315
315
 
316
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"]
316
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"]
317
317
  """Browser driver to use"""
318
318
 
319
319
  expected_status_codes: Iterable[int]
320
320
  """Expected HTTP status codes for successful requests"""
321
321
 
322
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
322
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
323
323
  """List of acceptable response formats in order of preference"""
324
324
 
325
325
  headers: Dict[str, Union[str, SequenceNotStr[str], None]]
@@ -868,6 +868,13 @@ class ClientExtractAsyncParams(TypedDict, total=False):
868
868
  ]
869
869
  """Locale for browser language and region settings"""
870
870
 
871
+ markdown_backend: Literal["full_page", "main_content"]
872
+ """Selects which markdown conversion strategy to use.
873
+
874
+ "full_page" converts the entire HTML page. "main_content" uses Mozilla
875
+ Readability to extract the main article content before converting.
876
+ """
877
+
871
878
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
872
879
  """HTTP method for the request"""
873
880
 
@@ -422,13 +422,13 @@ class Input(TypedDict, total=False):
422
422
  device: Literal["desktop", "mobile", "tablet"]
423
423
  """Device type for browser emulation"""
424
424
 
425
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"]
425
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"]
426
426
  """Browser driver to use"""
427
427
 
428
428
  expected_status_codes: Iterable[int]
429
429
  """Expected HTTP status codes for successful requests"""
430
430
 
431
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
431
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
432
432
  """List of acceptable response formats in order of preference"""
433
433
 
434
434
  headers: Dict[str, Union[str, SequenceNotStr[str], None]]
@@ -977,6 +977,13 @@ class Input(TypedDict, total=False):
977
977
  ]
978
978
  """Locale for browser language and region settings"""
979
979
 
980
+ markdown_backend: Literal["full_page", "main_content"]
981
+ """Selects which markdown conversion strategy to use.
982
+
983
+ "full_page" converts the entire HTML page. "main_content" uses Mozilla
984
+ Readability to extract the main article content before converting.
985
+ """
986
+
980
987
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
981
988
  """HTTP method for the request"""
982
989
 
@@ -1456,13 +1463,13 @@ class SharedInputs(TypedDict, total=False):
1456
1463
  device: Literal["desktop", "mobile", "tablet"]
1457
1464
  """Device type for browser emulation"""
1458
1465
 
1459
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"]
1466
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"]
1460
1467
  """Browser driver to use"""
1461
1468
 
1462
1469
  expected_status_codes: Iterable[int]
1463
1470
  """Expected HTTP status codes for successful requests"""
1464
1471
 
1465
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
1472
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
1466
1473
  """List of acceptable response formats in order of preference"""
1467
1474
 
1468
1475
  headers: Dict[str, Union[str, SequenceNotStr[str], None]]
@@ -2011,6 +2018,13 @@ class SharedInputs(TypedDict, total=False):
2011
2018
  ]
2012
2019
  """Locale for browser language and region settings"""
2013
2020
 
2021
+ markdown_backend: Literal["full_page", "main_content"]
2022
+ """Selects which markdown conversion strategy to use.
2023
+
2024
+ "full_page" converts the entire HTML page. "main_content" uses Mozilla
2025
+ Readability to extract the main article content before converting.
2026
+ """
2027
+
2014
2028
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
2015
2029
  """HTTP method for the request"""
2016
2030
 
@@ -310,13 +310,13 @@ class ClientExtractParams(TypedDict, total=False):
310
310
  device: Literal["desktop", "mobile", "tablet"]
311
311
  """Device type for browser emulation"""
312
312
 
313
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"]
313
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"]
314
314
  """Browser driver to use"""
315
315
 
316
316
  expected_status_codes: Iterable[int]
317
317
  """Expected HTTP status codes for successful requests"""
318
318
 
319
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
319
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
320
320
  """List of acceptable response formats in order of preference"""
321
321
 
322
322
  headers: Dict[str, Union[str, SequenceNotStr[str], None]]
@@ -865,6 +865,13 @@ class ClientExtractParams(TypedDict, total=False):
865
865
  ]
866
866
  """Locale for browser language and region settings"""
867
867
 
868
+ markdown_backend: Literal["full_page", "main_content"]
869
+ """Selects which markdown conversion strategy to use.
870
+
871
+ "full_page" converts the entire HTML page. "main_content" uses Mozilla
872
+ Readability to extract the main article content before converting.
873
+ """
874
+
868
875
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
869
876
  """HTTP method for the request"""
870
877
 
@@ -455,13 +455,13 @@ class ExtractOptions(TypedDict, total=False):
455
455
  device: Literal["desktop", "mobile", "tablet"]
456
456
  """Device type for browser emulation"""
457
457
 
458
- driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro"]
458
+ driver: Literal["vx6", "vx8", "vx8-pro", "vx10", "vx10-pro", "vx12", "vx12-pro", "media-vx6"]
459
459
  """Browser driver to use"""
460
460
 
461
461
  expected_status_codes: Iterable[int]
462
462
  """Expected HTTP status codes for successful requests"""
463
463
 
464
- formats: List[Literal["html", "markdown", "screenshot", "headers"]]
464
+ formats: List[Literal["html", "markdown", "screenshot", "headers", "links"]]
465
465
  """List of acceptable response formats in order of preference"""
466
466
 
467
467
  headers: Dict[str, Union[str, SequenceNotStr[str], None]]
@@ -1010,6 +1010,13 @@ class ExtractOptions(TypedDict, total=False):
1010
1010
  ]
1011
1011
  """Locale for browser language and region settings"""
1012
1012
 
1013
+ markdown_backend: Literal["full_page", "main_content"]
1014
+ """Selects which markdown conversion strategy to use.
1015
+
1016
+ "full_page" converts the entire HTML page. "main_content" uses Mozilla
1017
+ Readability to extract the main article content before converting.
1018
+ """
1019
+
1013
1020
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
1014
1021
  """HTTP method for the request"""
1015
1022
 
@@ -240,6 +240,9 @@ class Data(BaseModel):
240
240
  html: Optional[str] = None
241
241
  """The HTML content of the page."""
242
242
 
243
+ links: Optional[List[str]] = None
244
+ """List of all unique URLs found on the page."""
245
+
243
246
  markdown: Optional[str] = None
244
247
  """The Markdown version of the HTML content."""
245
248
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nimble_python
3
- Version: 0.16.0
3
+ Version: 0.17.0
4
4
  Summary: The official Python library for the nimble API
5
5
  Project-URL: Homepage, https://github.com/Nimbleway/nimble-python
6
6
  Project-URL: Repository, https://github.com/Nimbleway/nimble-python
@@ -1,19 +1,19 @@
1
1
  nimble_python/__init__.py,sha256=QWpm5I50gjoMEPPXsRiT0i3aqvsJby6NegnPHYrhCfs,2645
2
- nimble_python/_base_client.py,sha256=IR2D-rcC7Lz8L2c0xH_4dOX50NJEgUK0IzpKAbK11FU,73665
3
- nimble_python/_client.py,sha256=pP-ipRU38aLLPGVS1DISXGVJI474Qk_pQ2t1eT-fkvo,162929
2
+ nimble_python/_base_client.py,sha256=GVMopeALSRNw5GTAmUQfFvON3_tSKYw-MjKo8lDRfos,73932
3
+ nimble_python/_client.py,sha256=rdzJrl1TL5knL6v6YVjwCG7v0eh4oaFDVobyn2J5FnA,166509
4
4
  nimble_python/_compat.py,sha256=_9guQfzYnL3DNtudX5W7T2cdSskx89B5AFfhPQDxMUk,6811
5
5
  nimble_python/_constants.py,sha256=giUwuSKDnpjRLaHvgzErnXB3CSYjKMaI5xnBQJI-23s,464
6
6
  nimble_python/_exceptions.py,sha256=x1M0s_wI9jkJ5ARR07APUuDZfxl7BjQ53snlFU-jAoQ,3220
7
- nimble_python/_files.py,sha256=KnEzGi_O756MvKyJ4fOCW_u3JhOeWPQ4RsmDvqihDQU,3545
7
+ nimble_python/_files.py,sha256=GsGgJfC6PEiO0d4ItFBMQlBrK8LLNpTaBdHa7T3EMDE,5452
8
8
  nimble_python/_models.py,sha256=R3MpO2z4XhTAnD3ObEks32suRXleF1g7BEgQTOLIxTs,32112
9
- nimble_python/_qs.py,sha256=ExvESqZz_a5ZALZG1aR2rkzE35o9Rmk0yPMQtKN0DTo,4924
9
+ nimble_python/_qs.py,sha256=ZSfQv6arGBBoWlcy60zK1zmPE2Ije0i8kzKdnoxWUjU,4833
10
10
  nimble_python/_resource.py,sha256=TDtzjUMbNHRGQNq-4HJqce3T3THBD5Q7ESrONYPvKdI,1100
11
11
  nimble_python/_response.py,sha256=hrLOCn3D0gCapH31eR9BVOgBpTYliUTVUsdeWc1J76A,28983
12
12
  nimble_python/_streaming.py,sha256=StqGzUAIG9gQNCkRQnEAbN3IyiX4hDdCp2QR7_DANw8,10550
13
- nimble_python/_types.py,sha256=ZhbxWgjPQAqNY4ePn8aUJV26mm5oDBrZqskA1XyjlQ0,7601
14
- nimble_python/_version.py,sha256=Lb-CsoIL_IraYv5EV1eucIYPOAIePAbpXjQtYHRX5vQ,166
13
+ nimble_python/_types.py,sha256=rYClHO7LPhg_rRUmxWuC34Tf2mVPfmCZBIANhQ2fC3o,7709
14
+ nimble_python/_version.py,sha256=rVW0OGVVDRW6LcAJHqq6Eamsw-h_2lwUvwrSUUTaU5Y,166
15
15
  nimble_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- nimble_python/_utils/__init__.py,sha256=DJEWihdzRWrZI23vwocBkgvZCwn9iG7yV0c_9vKaysU,2355
16
+ nimble_python/_utils/__init__.py,sha256=nQq-iFa5YxTaWySaLigatew5rHgTR0M75FNYm4mrO1s,2313
17
17
  nimble_python/_utils/_compat.py,sha256=33246eDcl3pwL6kWsEhVuT4Akrd8gZEW9LPTm465ohk,1231
18
18
  nimble_python/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204
19
19
  nimble_python/_utils/_json.py,sha256=bl95uuIWwgSfXX-gP1trK_lDAPwJujYfJ05Cxo2SEC4,962
@@ -26,44 +26,44 @@ nimble_python/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-no
26
26
  nimble_python/_utils/_sync.py,sha256=HBnZkkBnzxtwOZe0212C4EyoRvxhTVtTrLFDz2_xVCg,1589
27
27
  nimble_python/_utils/_transform.py,sha256=NjCzmnfqYrsAikUHQig6N9QfuTVbKipuP3ur9mcNF-E,15951
28
28
  nimble_python/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,4786
29
- nimble_python/_utils/_utils.py,sha256=ugfUaneOK7I8h9b3656flwf5u_kthY0gvNuqvgOLoSU,12252
29
+ nimble_python/_utils/_utils.py,sha256=2nPOzDrPe2GADpLCRM4bNQS8Mu7LjEiCG_LJ2AAL6jg,13111
30
30
  nimble_python/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
31
31
  nimble_python/resources/__init__.py,sha256=Wh5Ns-DfhwV5B1NL1sauYogLEctZiqtJxLuEVQzUvVw,1876
32
- nimble_python/resources/agent.py,sha256=lA2lZIr6D_yOXtyBUVM8nEdR1vsVtRQCw138Z6cqU-4,37893
32
+ nimble_python/resources/agent.py,sha256=ecyhlZxtmnuGwHJwsouKPC4wwex8yipbVmySC2_MkCw,38726
33
33
  nimble_python/resources/batches.py,sha256=LxPXo7mpDcXmtJAv7prVnDpJHkhHypix6ZNSxh9kpQU,11688
34
34
  nimble_python/resources/crawl.py,sha256=GOMDGANIPAfay-J3ujW9ghlK-h-aIy99ChIKRN-pZK8,20651
35
35
  nimble_python/resources/tasks.py,sha256=8rV6YZNpx8rq_83L8UZvt7HlPlRhY8SGrbVQAFDjBtM,13021
36
36
  nimble_python/types/__init__.py,sha256=IMIgUvouuZ_JeP5p7HBJEnwQJdnSBt9YopnYPY8y2Tw,3327
37
- nimble_python/types/agent_generate_params.py,sha256=2YZjjZy7EDa-ISBftfjtgDh3wOba8UmmIVimYkILrG8,766
38
- nimble_python/types/agent_generate_response.py,sha256=NaLHpAtIMssf5o094YfpsphnQXRtdIyAbM7RiVa5ij8,664
39
- nimble_python/types/agent_get_generation_response.py,sha256=phzi3gsbrruea_eNcZIYJu9f8jYw26Nc_1sOyBsxIss,674
40
- nimble_python/types/agent_get_response.py,sha256=qQtjNztWHGWGJ_LKLn2-VtQlzv2AfkPvu0QaADRXqIg,1131
37
+ nimble_python/types/agent_generate_params.py,sha256=tPnfQ43Tux_uZUmSwr4tJFjVPDblkM1GfrHKwUa0tzA,1057
38
+ nimble_python/types/agent_generate_response.py,sha256=gSREaNTF0mlMsmKY14f3XI2f7hHS8u77XUkNGtgT98U,1325
39
+ nimble_python/types/agent_get_generation_response.py,sha256=1wZyJS54wSVa7fTzexX4Kd-G3ZWBZdYrlc2a1WbC9RM,1335
40
+ nimble_python/types/agent_get_response.py,sha256=Ok4Lz5FTMrP5ok6Ao4wQithGIwdkopPx1V4gnCOxGTc,1205
41
41
  nimble_python/types/agent_list_params.py,sha256=yUwgtiXrzfEXd3I--X_ISovY2BSHKXDvFZ2XUCUR9ew,670
42
42
  nimble_python/types/agent_list_response.py,sha256=kBEVOD9nlby5QsfaDkjPLXDJyQotph3HBo321jP8rb4,599
43
43
  nimble_python/types/agent_publish_params.py,sha256=GNnBk5qgq1T1JMotVJSVrY_bW6CJHRRuF5H-9ofj9zE,289
44
44
  nimble_python/types/agent_publish_response.py,sha256=VEyf8cMKVqgWRhOGPDuYevVhwPcE-oynoBc8UPoXVs8,247
45
- nimble_python/types/agent_run_async_params.py,sha256=kma0zTwV2RK4BIDnOAneQ1Vx1sLZqSezBs403ODdoaI,877
45
+ nimble_python/types/agent_run_async_params.py,sha256=2muyw_xyABrMVtZTIW9GLwBx8UO2LM3jU8B4fl3NLtI,886
46
46
  nimble_python/types/agent_run_async_response.py,sha256=aawcjhtW6FaWxIPcg-l-1yIhsQDLUoYOaoSf_MLJ67s,321
47
- nimble_python/types/agent_run_batch_params.py,sha256=R6yqYKx4w4-WFitwU1uPUu4DskYIc32c9NeKccm8UQ4,909
47
+ nimble_python/types/agent_run_batch_params.py,sha256=LCTCHB6kQAAy-TOZSan_Gzv5luYOhn-h2GikxP93nec,927
48
48
  nimble_python/types/agent_run_batch_response.py,sha256=0dtYQ6epddVqO2sdbeTDJyGGGAob24Cl3y1qC2k2AT4,1776
49
- nimble_python/types/agent_run_params.py,sha256=hC7CkPKvFoJf-KKIifJNKwDv-O_dS6Mk65k-A7up_RY,517
50
- nimble_python/types/agent_run_response.py,sha256=0kPTccMFP3kBu-9legG_dRP5HSDNiafqDgZkaYxnqFA,8155
49
+ nimble_python/types/agent_run_params.py,sha256=vn3zIe4k1eb-9rb2XhhBnJIRyr6NAxpO5PhfU0S6uWw,526
50
+ nimble_python/types/agent_run_response.py,sha256=TavoLSUAvUI7xhaOVaMuTBKOtrGyEeJlWnQ34xu-mxY,8247
51
51
  nimble_python/types/batch_get_response.py,sha256=5MIDHw3xt4UPdZY7eeYMDTJHbvyP4fCWNFMOLmuxcGM,2117
52
52
  nimble_python/types/batch_progress_response.py,sha256=oHEHXclc0NnI3TiGMalfElhZ15rfvR7nZRub9Gg_nqs,726
53
- nimble_python/types/client_extract_async_params.py,sha256=P26kzOH_UudChy5ZcCfoVz5YE-RVNCYifMAIAAeXErI,19339
54
- nimble_python/types/client_extract_batch_params.py,sha256=ZUrgFvuOMXsfsAEF-CmwZH4R5l48td3kzqZeZIGcMKM,38402
55
- nimble_python/types/client_extract_params.py,sha256=U5GAyppMNGKuBqwxhF_h-y9XCcGLlTj5bhzpYtvbeeY,18979
53
+ nimble_python/types/client_extract_async_params.py,sha256=-PftJe9MMk6fNJmAtPwSgVeN6KKw-_pQPnf_r_orjKc,19634
54
+ nimble_python/types/client_extract_batch_params.py,sha256=i_e4X2hxtdAm_DXjYSqBG-YHbw_M5YEAjX6XzP7btIw,38992
55
+ nimble_python/types/client_extract_params.py,sha256=Sd6kYzenVUB7jBdCodWcX-y5XuT7IukZRIKdwoCML3Y,19274
56
56
  nimble_python/types/client_map_params.py,sha256=UrYDjx_AjEdRVRkbJUaSBWAM7_bJIFZ95RzISFhidkA,13217
57
57
  nimble_python/types/client_search_params.py,sha256=KubZviMs3gOsbko4B_AHJ4ytomMfITVbrNmKDigYK2Y,2682
58
58
  nimble_python/types/crawl_list_params.py,sha256=Z6WaMJtkeBhRhK0ejXMHrQsSZI3OQWr-BlVFrPl3Lg4,527
59
59
  nimble_python/types/crawl_list_response.py,sha256=roMnzSv0XrpqfryMbeDGjvooMG3GsgJeW1yJS2g7RQs,2914
60
- nimble_python/types/crawl_run_params.py,sha256=jXrAV4sGi04eXhdEEGixqSG2Avb9bRJZKoNMFyfG_uU,20828
60
+ nimble_python/types/crawl_run_params.py,sha256=0LfsQIzgFtPCYoaLfO4h2ryWBleKTiIhRUYYfBG1sLs,21123
61
61
  nimble_python/types/crawl_run_response.py,sha256=DSi6cUZzEtCg7P6A0MrryrT7-mTe4mTPF7976OVx14M,2572
62
62
  nimble_python/types/crawl_status_response.py,sha256=rCNZkRNV65KzZpQCqbSX2FIW6kLh93C_lYMieS_UVEQ,2578
63
63
  nimble_python/types/crawl_terminate_response.py,sha256=Kw0udMxFAfBZcpKfIpK8Dt1wKyjFM5zUgGTiBNl1K4c,271
64
64
  nimble_python/types/extract_async_response.py,sha256=__3OdwaSstOvDzaQnMv2w4cWeD4GN0-2zZ7YxdNDufs,1786
65
65
  nimble_python/types/extract_batch_response.py,sha256=AjiTdf7eHP_kE3TCX7nDr2OdzdKWZyLDFkh3duZlNyg,1774
66
- nimble_python/types/extract_response.py,sha256=B1Md11774eeSLinT7Z-a6ImP_X-WrUGoYSD1mbZMPjo,8153
66
+ nimble_python/types/extract_response.py,sha256=67CkH4vOsZTUvGOWEMLANPBK_d2hgDcqlmEB_B3LhMk,8245
67
67
  nimble_python/types/map_response.py,sha256=rNCav8_9GXdbdNIaIQEWi6f8c_YduImddoCzxV4i4Os,607
68
68
  nimble_python/types/search_response.py,sha256=lykUe_ICfs-dvtcz9nnrcFrV80FkSkWrR3ljumCyH4Y,2210
69
69
  nimble_python/types/task_get_response.py,sha256=YMLwfZv2ejUu-4VePPCUkojzXDZ_4tYtYCem9JfQh0U,1563
@@ -98,7 +98,7 @@ nimble_python/types/shared_params/scroll_action.py,sha256=6PJMWVc9JhjyrXeAsFnvHR
98
98
  nimble_python/types/shared_params/wait_action.py,sha256=2jlVgNNZcC2M9tka3xbmLkDocCJuoX_1qIGOmd6a2DU,1046
99
99
  nimble_python/types/shared_params/wait_for_element_action.py,sha256=cKhvFe09G2aQBFKBXg00S43ykQqgNyTLYR1DoJmDvTA,1364
100
100
  nimble_python/types/shared_params/wait_for_navigation_action.py,sha256=oPAK3OxZObqJjDEh9v0_q1ntHfWqgtXOp_k-oX8ZiHE,1257
101
- nimble_python-0.16.0.dist-info/METADATA,sha256=be0K4zHYggpMamVI94U0JkITCx187hFZMdUYzxaXdrM,15816
102
- nimble_python-0.16.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
103
- nimble_python-0.16.0.dist-info/licenses/LICENSE,sha256=khtIpaYdgNbXXcmzsxlSL_iKmh160uTREZTiMQKCB24,11336
104
- nimble_python-0.16.0.dist-info/RECORD,,
101
+ nimble_python-0.17.0.dist-info/METADATA,sha256=0ZU6r9zx2r4CJ-wHv-ZY9i0gJyv_7ydND6BGckLgnPE,15816
102
+ nimble_python-0.17.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
103
+ nimble_python-0.17.0.dist-info/licenses/LICENSE,sha256=khtIpaYdgNbXXcmzsxlSL_iKmh160uTREZTiMQKCB24,11336
104
+ nimble_python-0.17.0.dist-info/RECORD,,