Skip to content

Commit e9b604e

Browse files
feat(api): support environments
1 parent 09cfed5 commit e9b604e

5 files changed

Lines changed: 117 additions & 24 deletions

File tree

.stats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 2
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/landingai%2Fade-f08049b74d593128128837d1e62b642bf68ee132184fddadc45d19fbc6d9f263.yml
33
openapi_spec_hash: 1cf6912d5249120cb222db3890587aab
4-
config_hash: 113449d0f864a31adaa7683357b11b72
4+
config_hash: ca433b766050897382afeb5c04a1bdee

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ from ade import Ade
3030

3131
client = Ade(
3232
apikey=os.environ.get("ADE_API_KEY"), # This is the default and can be omitted
33+
# defaults to "production".
34+
environment="eu-production",
3335
)
3436

35-
response = client.ade.parse(
36-
document=b"/path/to/file",
37-
)
37+
response = client.ade.parse()
3838
print(response.chunks)
3939
```
4040

@@ -54,13 +54,13 @@ from ade import AsyncAde
5454

5555
client = AsyncAde(
5656
apikey=os.environ.get("ADE_API_KEY"), # This is the default and can be omitted
57+
# defaults to "production".
58+
environment="eu-production",
5759
)
5860

5961

6062
async def main() -> None:
61-
response = await client.ade.parse(
62-
document=b"/path/to/file",
63-
)
63+
response = await client.ade.parse()
6464
print(response.chunks)
6565

6666

@@ -93,9 +93,7 @@ async def main() -> None:
9393
apikey="My Apikey",
9494
http_client=DefaultAioHttpClient(),
9595
) as client:
96-
response = await client.ade.parse(
97-
document=b"/path/to/file",
98-
)
96+
response = await client.ade.parse()
9997
print(response.chunks)
10098

10199

src/ade/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,18 @@
55
from . import types
66
from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
77
from ._utils import file_from_path
8-
from ._client import Ade, Client, Stream, Timeout, AsyncAde, Transport, AsyncClient, AsyncStream, RequestOptions
8+
from ._client import (
9+
ENVIRONMENTS,
10+
Ade,
11+
Client,
12+
Stream,
13+
Timeout,
14+
AsyncAde,
15+
Transport,
16+
AsyncClient,
17+
AsyncStream,
18+
RequestOptions,
19+
)
920
from ._models import BaseModel
1021
from ._version import __title__, __version__
1122
from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse
@@ -61,6 +72,7 @@
6172
"AsyncStream",
6273
"Ade",
6374
"AsyncAde",
75+
"ENVIRONMENTS",
6476
"file_from_path",
6577
"BaseModel",
6678
"DEFAULT_TIMEOUT",

src/ade/_client.py

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from __future__ import annotations
44

55
import os
6-
from typing import Any, Union, Mapping
7-
from typing_extensions import Self, override
6+
from typing import Any, Dict, Union, Mapping, cast
7+
from typing_extensions import Self, Literal, override
88

99
import httpx
1010

@@ -30,7 +30,22 @@
3030
AsyncAPIClient,
3131
)
3232

33-
__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Ade", "AsyncAde", "Client", "AsyncClient"]
33+
__all__ = [
34+
"ENVIRONMENTS",
35+
"Timeout",
36+
"Transport",
37+
"ProxiesTypes",
38+
"RequestOptions",
39+
"Ade",
40+
"AsyncAde",
41+
"Client",
42+
"AsyncClient",
43+
]
44+
45+
ENVIRONMENTS: Dict[str, str] = {
46+
"production": "https://api.va.landing.ai",
47+
"eu-production": "https://va.eu-west-1.landing.ai",
48+
}
3449

3550

3651
class Ade(SyncAPIClient):
@@ -41,11 +56,14 @@ class Ade(SyncAPIClient):
4156
# client options
4257
apikey: str
4358

59+
_environment: Literal["production", "eu-production"] | NotGiven
60+
4461
def __init__(
4562
self,
4663
*,
4764
apikey: str | None = None,
48-
base_url: str | httpx.URL | None = None,
65+
environment: Literal["production", "eu-production"] | NotGiven = NOT_GIVEN,
66+
base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
4967
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
5068
max_retries: int = DEFAULT_MAX_RETRIES,
5169
default_headers: Mapping[str, str] | None = None,
@@ -76,10 +94,31 @@ def __init__(
7694
)
7795
self.apikey = apikey
7896

79-
if base_url is None:
80-
base_url = os.environ.get("ADE_BASE_URL")
81-
if base_url is None:
82-
base_url = f"https://api.va.landing.ai"
97+
self._environment = environment
98+
99+
base_url_env = os.environ.get("ADE_BASE_URL")
100+
if is_given(base_url) and base_url is not None:
101+
# cast required because mypy doesn't understand the type narrowing
102+
base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
103+
elif is_given(environment):
104+
if base_url_env and base_url is not None:
105+
raise ValueError(
106+
"Ambiguous URL; The `ADE_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
107+
)
108+
109+
try:
110+
base_url = ENVIRONMENTS[environment]
111+
except KeyError as exc:
112+
raise ValueError(f"Unknown environment: {environment}") from exc
113+
elif base_url_env is not None:
114+
base_url = base_url_env
115+
else:
116+
self._environment = environment = "production"
117+
118+
try:
119+
base_url = ENVIRONMENTS[environment]
120+
except KeyError as exc:
121+
raise ValueError(f"Unknown environment: {environment}") from exc
83122

84123
super().__init__(
85124
version=__version__,
@@ -120,6 +159,7 @@ def copy(
120159
self,
121160
*,
122161
apikey: str | None = None,
162+
environment: Literal["production", "eu-production"] | None = None,
123163
base_url: str | httpx.URL | None = None,
124164
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
125165
http_client: httpx.Client | None = None,
@@ -155,6 +195,7 @@ def copy(
155195
return self.__class__(
156196
apikey=apikey or self.apikey,
157197
base_url=base_url or self.base_url,
198+
environment=environment or self._environment,
158199
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
159200
http_client=http_client,
160201
max_retries=max_retries if is_given(max_retries) else self.max_retries,
@@ -209,11 +250,14 @@ class AsyncAde(AsyncAPIClient):
209250
# client options
210251
apikey: str
211252

253+
_environment: Literal["production", "eu-production"] | NotGiven
254+
212255
def __init__(
213256
self,
214257
*,
215258
apikey: str | None = None,
216-
base_url: str | httpx.URL | None = None,
259+
environment: Literal["production", "eu-production"] | NotGiven = NOT_GIVEN,
260+
base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
217261
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
218262
max_retries: int = DEFAULT_MAX_RETRIES,
219263
default_headers: Mapping[str, str] | None = None,
@@ -244,10 +288,31 @@ def __init__(
244288
)
245289
self.apikey = apikey
246290

247-
if base_url is None:
248-
base_url = os.environ.get("ADE_BASE_URL")
249-
if base_url is None:
250-
base_url = f"https://api.va.landing.ai"
291+
self._environment = environment
292+
293+
base_url_env = os.environ.get("ADE_BASE_URL")
294+
if is_given(base_url) and base_url is not None:
295+
# cast required because mypy doesn't understand the type narrowing
296+
base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
297+
elif is_given(environment):
298+
if base_url_env and base_url is not None:
299+
raise ValueError(
300+
"Ambiguous URL; The `ADE_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
301+
)
302+
303+
try:
304+
base_url = ENVIRONMENTS[environment]
305+
except KeyError as exc:
306+
raise ValueError(f"Unknown environment: {environment}") from exc
307+
elif base_url_env is not None:
308+
base_url = base_url_env
309+
else:
310+
self._environment = environment = "production"
311+
312+
try:
313+
base_url = ENVIRONMENTS[environment]
314+
except KeyError as exc:
315+
raise ValueError(f"Unknown environment: {environment}") from exc
251316

252317
super().__init__(
253318
version=__version__,
@@ -288,6 +353,7 @@ def copy(
288353
self,
289354
*,
290355
apikey: str | None = None,
356+
environment: Literal["production", "eu-production"] | None = None,
291357
base_url: str | httpx.URL | None = None,
292358
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
293359
http_client: httpx.AsyncClient | None = None,
@@ -323,6 +389,7 @@ def copy(
323389
return self.__class__(
324390
apikey=apikey or self.apikey,
325391
base_url=base_url or self.base_url,
392+
environment=environment or self._environment,
326393
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
327394
http_client=http_client,
328395
max_retries=max_retries if is_given(max_retries) else self.max_retries,

tests/test_client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,14 @@ def test_base_url_env(self) -> None:
550550
client = Ade(apikey=apikey, _strict_response_validation=True)
551551
assert client.base_url == "http://localhost:5000/from/env/"
552552

553+
# explicit environment arg requires explicitness
554+
with update_env(ADE_BASE_URL="http://localhost:5000/from/env"):
555+
with pytest.raises(ValueError, match=r"you must pass base_url=None"):
556+
Ade(apikey=apikey, _strict_response_validation=True, environment="production")
557+
558+
client = Ade(base_url=None, apikey=apikey, _strict_response_validation=True, environment="production")
559+
assert str(client.base_url).startswith("https://api.va.landing.ai")
560+
553561
@pytest.mark.parametrize(
554562
"client",
555563
[
@@ -1347,6 +1355,14 @@ def test_base_url_env(self) -> None:
13471355
client = AsyncAde(apikey=apikey, _strict_response_validation=True)
13481356
assert client.base_url == "http://localhost:5000/from/env/"
13491357

1358+
# explicit environment arg requires explicitness
1359+
with update_env(ADE_BASE_URL="http://localhost:5000/from/env"):
1360+
with pytest.raises(ValueError, match=r"you must pass base_url=None"):
1361+
AsyncAde(apikey=apikey, _strict_response_validation=True, environment="production")
1362+
1363+
client = AsyncAde(base_url=None, apikey=apikey, _strict_response_validation=True, environment="production")
1364+
assert str(client.base_url).startswith("https://api.va.landing.ai")
1365+
13501366
@pytest.mark.parametrize(
13511367
"client",
13521368
[

0 commit comments

Comments
 (0)