Skip to content

Commit

Permalink
feat(api): api update (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-app[bot] committed Oct 14, 2024
1 parent ac4e1d2 commit 2abd2f1
Show file tree
Hide file tree
Showing 26 changed files with 924 additions and 406 deletions.
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
configured_endpoints: 7
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-5d0034cce7bb0c697f3ec08ab4d585002898a26e9e0e03f42ae2515f1cc56087.yml
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/arcade-ai%2Farcade-engine-2f4d672c34ee530fb7290e2fb799d907aba7d9e47030659422f4e7760625be90.yml
68 changes: 55 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,35 @@ The REST API documentation can be found on [arcade-ai.com](https://arcade-ai.com
## Installation

```sh
# install from PyPI
pip install --pre arcadepy
# 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 arcadepy`
## Usage

The full API of this library can be found in [api.md](api.md).

```python
import os
from arcadepy import ArcadeAI

client = ArcadeAI(
# This is the default and can be omitted
api_key=os.environ.get("ARCADE_API_KEY"),
# defaults to "production".
environment="staging",
)

response = client.tools.execute(
tool_response = client.tools.execute(
inputs="[object Object]",
tool_name="Google.ListEmails",
tool_version="0.1.0",
user_id="dev@arcade-ai.com",
user_id="user@example.com",
)
print(response.invocation_id)
print(tool_response.invocation_id)
```

While you can provide an `api_key` keyword argument,
Expand All @@ -50,23 +56,26 @@ so that your API Key is not stored in source control.
Simply import `AsyncArcadeAI` instead of `ArcadeAI` and use `await` with each API call:

```python
import os
import asyncio
from arcadepy import AsyncArcadeAI

client = AsyncArcadeAI(
# This is the default and can be omitted
api_key=os.environ.get("ARCADE_API_KEY"),
# defaults to "production".
environment="staging",
)


async def main() -> None:
response = await client.tools.execute(
tool_response = await client.tools.execute(
inputs="[object Object]",
tool_name="Google.ListEmails",
tool_version="0.1.0",
user_id="dev@arcade-ai.com",
user_id="user@example.com",
)
print(response.invocation_id)
print(tool_response.invocation_id)


asyncio.run(main())
Expand Down Expand Up @@ -99,7 +108,14 @@ from arcadepy import ArcadeAI
client = ArcadeAI()

try:
client.chat.completions()
client.chat.completions(
messages=[
{
"role": "user",
"content": "Hello, how can I use Arcade AI?",
}
],
)
except arcadepy.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx.
Expand Down Expand Up @@ -142,7 +158,14 @@ client = ArcadeAI(
)

# Or, configure per-request:
client.with_options(max_retries=5).chat.completions()
client.with_options(max_retries=5).chat.completions(
messages=[
{
"role": "user",
"content": "Hello, how can I use Arcade AI?",
}
],
)
```

### Timeouts
Expand All @@ -165,7 +188,14 @@ client = ArcadeAI(
)

# Override per-request:
client.with_options(timeout=5.0).chat.completions()
client.with_options(timeout=5.0).chat.completions(
messages=[
{
"role": "user",
"content": "Hello, how can I use Arcade AI?",
}
],
)
```

On timeout, an `APITimeoutError` is thrown.
Expand Down Expand Up @@ -204,7 +234,12 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to
from arcadepy import ArcadeAI

client = ArcadeAI()
response = client.chat.with_raw_response.completions()
response = client.chat.with_raw_response.completions(
messages=[{
"role": "user",
"content": "Hello, how can I use Arcade AI?",
}],
)
print(response.headers.get('X-My-Header'))

chat = response.parse() # get the object that `chat.completions()` would have returned
Expand All @@ -222,7 +257,14 @@ 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.chat.with_streaming_response.completions(
messages=[
{
"role": "user",
"content": "Hello, how can I use Arcade AI?",
}
],
) as response:
print(response.headers.get("X-My-Header"))

for line in response.iter_lines():
Expand Down
24 changes: 15 additions & 9 deletions api.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
# Shared Types

```python
from arcadepy.types import AuthorizationResponse
from arcadepy.types import Error
```

# Auth

Types:

```python
from arcadepy.types import AuthorizationResponse
```

Methods:

- <code title="post /v1/auth/authorize">client.auth.<a href="./src/arcadepy/resources/auth.py">authorization</a>(\*\*<a href="src/arcadepy/types/auth_authorization_params.py">params</a>) -> <a href="./src/arcadepy/types/shared/authorization_response.py">AuthorizationResponse</a></code>
- <code title="get /v1/auth/status">client.auth.<a href="./src/arcadepy/resources/auth.py">status</a>(\*\*<a href="src/arcadepy/types/auth_status_params.py">params</a>) -> <a href="./src/arcadepy/types/shared/authorization_response.py">AuthorizationResponse</a></code>
- <code title="post /v1/auth/authorize">client.auth.<a href="./src/arcadepy/resources/auth.py">authorize</a>(\*\*<a href="src/arcadepy/types/auth_authorize_params.py">params</a>) -> <a href="./src/arcadepy/types/authorization_response.py">AuthorizationResponse</a></code>
- <code title="get /v1/auth/status">client.auth.<a href="./src/arcadepy/resources/auth.py">status</a>(\*\*<a href="src/arcadepy/types/auth_status_params.py">params</a>) -> <a href="./src/arcadepy/types/authorization_response.py">AuthorizationResponse</a></code>

# Chat

Types:

```python
from arcadepy.types import ChatResponse
from arcadepy.types import ChatMessage, ChatRequest, ChatResponse
```

Methods:
Expand All @@ -33,18 +39,18 @@ from arcadepy.types import HealthSchema

Methods:

- <code title="get /v1/health">client.health.<a href="./src/arcadepy/resources/health.py">list</a>() -> <a href="./src/arcadepy/types/health_schema.py">HealthSchema</a></code>
- <code title="get /v1/health">client.health.<a href="./src/arcadepy/resources/health.py">check</a>() -> <a href="./src/arcadepy/types/health_schema.py">HealthSchema</a></code>

# Tools

Types:

```python
from arcadepy.types import Definition, Response
from arcadepy.types import AuthorizeToolRequest, ExecuteToolRequest, ToolDefinition, ToolResponse
```

Methods:

- <code title="get /v1/tools/definition">client.tools.<a href="./src/arcadepy/resources/tools.py">retrieve</a>(\*\*<a href="src/arcadepy/types/tool_retrieve_params.py">params</a>) -> <a href="./src/arcadepy/types/definition.py">Definition</a></code>
- <code title="post /v1/tools/authorize">client.tools.<a href="./src/arcadepy/resources/tools.py">authorize</a>(\*\*<a href="src/arcadepy/types/tool_authorize_params.py">params</a>) -> <a href="./src/arcadepy/types/shared/authorization_response.py">AuthorizationResponse</a></code>
- <code title="post /v1/tools/execute">client.tools.<a href="./src/arcadepy/resources/tools.py">execute</a>(\*\*<a href="src/arcadepy/types/tool_execute_params.py">params</a>) -> <a href="./src/arcadepy/types/response.py">Response</a></code>
- <code title="post /v1/tools/authorize">client.tools.<a href="./src/arcadepy/resources/tools.py">authorize</a>(\*\*<a href="src/arcadepy/types/tool_authorize_params.py">params</a>) -> <a href="./src/arcadepy/types/authorization_response.py">AuthorizationResponse</a></code>
- <code title="post /v1/tools/execute">client.tools.<a href="./src/arcadepy/resources/tools.py">execute</a>(\*\*<a href="src/arcadepy/types/tool_execute_params.py">params</a>) -> <a href="./src/arcadepy/types/tool_response.py">ToolResponse</a></code>
- <code title="get /v1/tools/definition">client.tools.<a href="./src/arcadepy/resources/tools.py">retrieve_definition</a>(\*\*<a href="src/arcadepy/types/tool_retrieve_definition_params.py">params</a>) -> <a href="./src/arcadepy/types/tool_definition.py">ToolDefinition</a></code>
45 changes: 15 additions & 30 deletions src/arcadepy/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from ._types import (
NOT_GIVEN,
Omit,
Headers,
Timeout,
NotGiven,
Transport,
Expand All @@ -26,7 +25,7 @@
)
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import APIStatusError
from ._exceptions import ArcadeAIError, APIStatusError
from ._base_client import (
DEFAULT_MAX_RETRIES,
SyncAPIClient,
Expand Down Expand Up @@ -61,7 +60,7 @@ class ArcadeAI(SyncAPIClient):
with_streaming_response: ArcadeAIWithStreamedResponse

# client options
api_key: str | None
api_key: str

_environment: Literal["production", "staging"] | NotGiven

Expand Down Expand Up @@ -95,6 +94,10 @@ def __init__(
"""
if api_key is None:
api_key = os.environ.get("ARCADE_API_KEY")
if api_key is None:
raise ArcadeAIError(
"The api_key client option must be set either by passing api_key to the client or by setting the ARCADE_API_KEY environment variable"
)
self.api_key = api_key

self._environment = environment
Expand Down Expand Up @@ -134,6 +137,8 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)

self._idempotency_header = "Idempotency-Key"

self.auth = resources.AuthResource(self)
self.chat = resources.ChatResource(self)
self.health = resources.HealthResource(self)
Expand All @@ -150,8 +155,6 @@ def qs(self) -> Querystring:
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"Authorization": api_key}

@property
Expand All @@ -163,17 +166,6 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}

@override
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
if self.api_key and headers.get("Authorization"):
return
if isinstance(custom_headers.get("Authorization"), Omit):
return

raise TypeError(
'"Could not resolve authentication method. Expected the api_key to be set. Or for the `Authorization` headers to be explicitly omitted"'
)

def copy(
self,
*,
Expand Down Expand Up @@ -270,7 +262,7 @@ class AsyncArcadeAI(AsyncAPIClient):
with_streaming_response: AsyncArcadeAIWithStreamedResponse

# client options
api_key: str | None
api_key: str

_environment: Literal["production", "staging"] | NotGiven

Expand Down Expand Up @@ -304,6 +296,10 @@ def __init__(
"""
if api_key is None:
api_key = os.environ.get("ARCADE_API_KEY")
if api_key is None:
raise ArcadeAIError(
"The api_key client option must be set either by passing api_key to the client or by setting the ARCADE_API_KEY environment variable"
)
self.api_key = api_key

self._environment = environment
Expand Down Expand Up @@ -343,6 +339,8 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)

self._idempotency_header = "Idempotency-Key"

self.auth = resources.AsyncAuthResource(self)
self.chat = resources.AsyncChatResource(self)
self.health = resources.AsyncHealthResource(self)
Expand All @@ -359,8 +357,6 @@ def qs(self) -> Querystring:
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"Authorization": api_key}

@property
Expand All @@ -372,17 +368,6 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}

@override
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
if self.api_key and headers.get("Authorization"):
return
if isinstance(custom_headers.get("Authorization"), Omit):
return

raise TypeError(
'"Could not resolve authentication method. Expected the api_key to be set. Or for the `Authorization` headers to be explicitly omitted"'
)

def copy(
self,
*,
Expand Down
Loading

0 comments on commit 2abd2f1

Please sign in to comment.