Skip to content

Commit

Permalink
feat(api): api update (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-app[bot] authored and stainless-bot committed Oct 13, 2024
1 parent 3cc79b2 commit 9817f3b
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 37 deletions.
52 changes: 39 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ The REST API documentation can be found on [arcade-ai.com](https://arcade-ai.com
## Installation

```sh
# install from PyPI
pip install --pre arcade-py
# install from the production repo
pip install git+ssh://[email protected]/ArcadeAI/arcade-py.git
```

> [!NOTE]
> Once this package is [published to PyPI](https://app.stainlessapi.com/docs/guides/publish), this will become: `pip install --pre arcade-py`
## Usage

The full API of this library can be found in [api.md](api.md).
Expand All @@ -28,8 +31,11 @@ from arcade_engine import ArcadeEngine

client = ArcadeEngine()

chat_response = client.chat.completions()
print(chat_response.id)
authorization_response = client.auth.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
)
print(authorization_response.authorization_id)
```

While you can provide an `api_key` keyword argument,
Expand All @@ -49,8 +55,11 @@ client = AsyncArcadeEngine()


async def main() -> None:
chat_response = await client.chat.completions()
print(chat_response.id)
authorization_response = await client.auth.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
)
print(authorization_response.authorization_id)


asyncio.run(main())
Expand Down Expand Up @@ -83,7 +92,10 @@ from arcade_engine import ArcadeEngine
client = ArcadeEngine()

try:
client.chat.completions()
client.auth.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
)
except arcade_engine.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx.
Expand Down Expand Up @@ -126,7 +138,10 @@ client = ArcadeEngine(
)

# Or, configure per-request:
client.with_options(max_retries=5).chat.completions()
client.with_options(max_retries=5).auth.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
)
```

### Timeouts
Expand All @@ -149,7 +164,10 @@ client = ArcadeEngine(
)

# Override per-request:
client.with_options(timeout=5.0).chat.completions()
client.with_options(timeout=5.0).auth.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
)
```

On timeout, an `APITimeoutError` is thrown.
Expand Down Expand Up @@ -188,11 +206,16 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to
from arcade_engine import ArcadeEngine

client = ArcadeEngine()
response = client.chat.with_raw_response.completions()
response = client.auth.with_raw_response.authorization(
auth_requirement={
"provider": "provider"
},
user_id="user_id",
)
print(response.headers.get('X-My-Header'))

chat = response.parse() # get the object that `chat.completions()` would have returned
print(chat.id)
auth = response.parse() # get the object that `auth.authorization()` would have returned
print(auth.authorization_id)
```

These methods return an [`APIResponse`](https://github.com/ArcadeAI/arcade-py/tree/main/src/arcade_engine/_response.py) object.
Expand All @@ -206,7 +229,10 @@ The above interface eagerly reads the full response body when you make the reque
To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.

```python
with client.chat.with_streaming_response.completions() as response:
with client.auth.with_streaming_response.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
) as response:
print(response.headers.get("X-My-Header"))

for line in response.iter_lines():
Expand Down
68 changes: 44 additions & 24 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,12 +707,12 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str
@mock.patch("arcade_engine._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
respx_mock.post("/v1/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error"))
respx_mock.post("/v1/auth/authorize").mock(side_effect=httpx.TimeoutException("Test timeout error"))

with pytest.raises(APITimeoutError):
self.client.post(
"/v1/chat/completions",
body=cast(object, dict()),
"/v1/auth/authorize",
body=cast(object, dict(auth_requirement={"provider": "provider"}, user_id="user_id")),
cast_to=httpx.Response,
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
)
Expand All @@ -722,12 +722,12 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No
@mock.patch("arcade_engine._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
respx_mock.post("/v1/chat/completions").mock(return_value=httpx.Response(500))
respx_mock.post("/v1/auth/authorize").mock(return_value=httpx.Response(500))

with pytest.raises(APIStatusError):
self.client.post(
"/v1/chat/completions",
body=cast(object, dict()),
"/v1/auth/authorize",
body=cast(object, dict(auth_requirement={"provider": "provider"}, user_id="user_id")),
cast_to=httpx.Response,
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
)
Expand All @@ -749,9 +749,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = client.chat.with_raw_response.completions()
response = client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"}, user_id="user_id"
)

assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
Expand All @@ -773,9 +775,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = client.chat.with_raw_response.completions(extra_headers={"x-stainless-retry-count": Omit()})
response = client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
extra_headers={"x-stainless-retry-count": Omit()},
)

assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0

Expand All @@ -796,9 +802,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = client.chat.with_raw_response.completions(extra_headers={"x-stainless-retry-count": "42"})
response = client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
extra_headers={"x-stainless-retry-count": "42"},
)

assert response.http_request.headers.get("x-stainless-retry-count") == "42"

Expand Down Expand Up @@ -1464,12 +1474,12 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte
@mock.patch("arcade_engine._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
respx_mock.post("/v1/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error"))
respx_mock.post("/v1/auth/authorize").mock(side_effect=httpx.TimeoutException("Test timeout error"))

with pytest.raises(APITimeoutError):
await self.client.post(
"/v1/chat/completions",
body=cast(object, dict()),
"/v1/auth/authorize",
body=cast(object, dict(auth_requirement={"provider": "provider"}, user_id="user_id")),
cast_to=httpx.Response,
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
)
Expand All @@ -1479,12 +1489,12 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter)
@mock.patch("arcade_engine._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
respx_mock.post("/v1/chat/completions").mock(return_value=httpx.Response(500))
respx_mock.post("/v1/auth/authorize").mock(return_value=httpx.Response(500))

with pytest.raises(APIStatusError):
await self.client.post(
"/v1/chat/completions",
body=cast(object, dict()),
"/v1/auth/authorize",
body=cast(object, dict(auth_requirement={"provider": "provider"}, user_id="user_id")),
cast_to=httpx.Response,
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
)
Expand All @@ -1509,9 +1519,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = await client.chat.with_raw_response.completions()
response = await client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"}, user_id="user_id"
)

assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
Expand All @@ -1534,9 +1546,13 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = await client.chat.with_raw_response.completions(extra_headers={"x-stainless-retry-count": Omit()})
response = await client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
extra_headers={"x-stainless-retry-count": Omit()},
)

assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0

Expand All @@ -1558,8 +1574,12 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)

respx_mock.post("/v1/chat/completions").mock(side_effect=retry_handler)
respx_mock.post("/v1/auth/authorize").mock(side_effect=retry_handler)

response = await client.chat.with_raw_response.completions(extra_headers={"x-stainless-retry-count": "42"})
response = await client.auth.with_raw_response.authorization(
auth_requirement={"provider": "provider"},
user_id="user_id",
extra_headers={"x-stainless-retry-count": "42"},
)

assert response.http_request.headers.get("x-stainless-retry-count") == "42"

0 comments on commit 9817f3b

Please sign in to comment.