21st-sdk 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 21st_sdk-0.0.1/PKG-INFO +86 -0
- 21st_sdk-0.0.1/README.md +74 -0
- 21st_sdk-0.0.1/pyproject.toml +35 -0
- 21st_sdk-0.0.1/setup.cfg +4 -0
- 21st_sdk-0.0.1/src/21st_sdk.egg-info/PKG-INFO +86 -0
- 21st_sdk-0.0.1/src/21st_sdk.egg-info/SOURCES.txt +10 -0
- 21st_sdk-0.0.1/src/21st_sdk.egg-info/dependency_links.txt +1 -0
- 21st_sdk-0.0.1/src/21st_sdk.egg-info/requires.txt +1 -0
- 21st_sdk-0.0.1/src/21st_sdk.egg-info/top_level.txt +1 -0
- 21st_sdk-0.0.1/src/twentyfirst_sdk/__init__.py +35 -0
- 21st_sdk-0.0.1/src/twentyfirst_sdk/client.py +292 -0
- 21st_sdk-0.0.1/src/twentyfirst_sdk/types.py +41 -0
21st_sdk-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: 21st-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Server-side Python SDK for 21st Agents
|
|
5
|
+
Author: 21st.dev
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://21st.dev/agents
|
|
8
|
+
Keywords: ai,agent,sandbox,21st,21st.dev,sdk,python
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: requests>=2.31.0
|
|
12
|
+
|
|
13
|
+
# 21st-sdk Python
|
|
14
|
+
|
|
15
|
+
Server-side Python SDK for [21st Agents](https://21st.dev/agents). Manage sandboxes, threads, and tokens programmatically.
|
|
16
|
+
|
|
17
|
+
Method arguments use Python-style `snake_case`, but SDK responses keep the relay's `camelCase` field names.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install 21st-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import os
|
|
29
|
+
|
|
30
|
+
from twentyfirst_sdk import AgentClient
|
|
31
|
+
|
|
32
|
+
client = AgentClient(api_key=os.environ["API_KEY_21ST"]) # an_sk_...
|
|
33
|
+
|
|
34
|
+
# Create a sandbox for your agent
|
|
35
|
+
sandbox = client.sandboxes.create(agent="my-agent")
|
|
36
|
+
|
|
37
|
+
# Create a thread
|
|
38
|
+
thread = client.threads.create(sandbox_id=sandbox.id, name="Review PR #42")
|
|
39
|
+
|
|
40
|
+
# Generate a short-lived token for browser clients
|
|
41
|
+
token = client.tokens.create(agent="my-agent", expires_in="1h")
|
|
42
|
+
print(token.expiresAt)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### `AgentClient(api_key, base_url=...)`
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
AgentClient(
|
|
51
|
+
api_key="...", # Your an_sk_ API key
|
|
52
|
+
base_url="...", # Optional, default: "https://relay.an.dev"
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `client.sandboxes`
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `create(agent=...)` | Create a new sandbox for an agent |
|
|
61
|
+
| `get(sandbox_id)` | Get sandbox details (status, threads, agent info) |
|
|
62
|
+
| `delete(sandbox_id)` | Delete a sandbox |
|
|
63
|
+
| `exec(sandbox_id, command, ...)` | Run a command in a sandbox |
|
|
64
|
+
| `files.write(sandbox_id, files)` | Write files into a sandbox |
|
|
65
|
+
| `files.read(sandbox_id, path)` | Read a file from a sandbox |
|
|
66
|
+
| `git.clone(sandbox_id, url, ...)` | Clone a repository into a sandbox |
|
|
67
|
+
|
|
68
|
+
### `client.threads`
|
|
69
|
+
|
|
70
|
+
| Method | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| `list(sandbox_id)` | List all threads in a sandbox |
|
|
73
|
+
| `create(sandbox_id, name=...)` | Create a new thread |
|
|
74
|
+
| `get(sandbox_id, thread_id)` | Get thread with messages |
|
|
75
|
+
| `delete(sandbox_id, thread_id)` | Delete a thread |
|
|
76
|
+
| `run(agent, messages, ...)` | Run a thread and return the raw streaming response |
|
|
77
|
+
|
|
78
|
+
### `client.tokens`
|
|
79
|
+
|
|
80
|
+
| Method | Description |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `create(agent=..., user_id=..., expires_in=...)` | Create a short-lived JWT (default: `"1h"`) |
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
21st_sdk-0.0.1/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# 21st-sdk Python
|
|
2
|
+
|
|
3
|
+
Server-side Python SDK for [21st Agents](https://21st.dev/agents). Manage sandboxes, threads, and tokens programmatically.
|
|
4
|
+
|
|
5
|
+
Method arguments use Python-style `snake_case`, but SDK responses keep the relay's `camelCase` field names.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install 21st-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from twentyfirst_sdk import AgentClient
|
|
19
|
+
|
|
20
|
+
client = AgentClient(api_key=os.environ["API_KEY_21ST"]) # an_sk_...
|
|
21
|
+
|
|
22
|
+
# Create a sandbox for your agent
|
|
23
|
+
sandbox = client.sandboxes.create(agent="my-agent")
|
|
24
|
+
|
|
25
|
+
# Create a thread
|
|
26
|
+
thread = client.threads.create(sandbox_id=sandbox.id, name="Review PR #42")
|
|
27
|
+
|
|
28
|
+
# Generate a short-lived token for browser clients
|
|
29
|
+
token = client.tokens.create(agent="my-agent", expires_in="1h")
|
|
30
|
+
print(token.expiresAt)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
### `AgentClient(api_key, base_url=...)`
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
AgentClient(
|
|
39
|
+
api_key="...", # Your an_sk_ API key
|
|
40
|
+
base_url="...", # Optional, default: "https://relay.an.dev"
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### `client.sandboxes`
|
|
45
|
+
|
|
46
|
+
| Method | Description |
|
|
47
|
+
|--------|-------------|
|
|
48
|
+
| `create(agent=...)` | Create a new sandbox for an agent |
|
|
49
|
+
| `get(sandbox_id)` | Get sandbox details (status, threads, agent info) |
|
|
50
|
+
| `delete(sandbox_id)` | Delete a sandbox |
|
|
51
|
+
| `exec(sandbox_id, command, ...)` | Run a command in a sandbox |
|
|
52
|
+
| `files.write(sandbox_id, files)` | Write files into a sandbox |
|
|
53
|
+
| `files.read(sandbox_id, path)` | Read a file from a sandbox |
|
|
54
|
+
| `git.clone(sandbox_id, url, ...)` | Clone a repository into a sandbox |
|
|
55
|
+
|
|
56
|
+
### `client.threads`
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `list(sandbox_id)` | List all threads in a sandbox |
|
|
61
|
+
| `create(sandbox_id, name=...)` | Create a new thread |
|
|
62
|
+
| `get(sandbox_id, thread_id)` | Get thread with messages |
|
|
63
|
+
| `delete(sandbox_id, thread_id)` | Delete a thread |
|
|
64
|
+
| `run(agent, messages, ...)` | Run a thread and return the raw streaming response |
|
|
65
|
+
|
|
66
|
+
### `client.tokens`
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|--------|-------------|
|
|
70
|
+
| `create(agent=..., user_id=..., expires_in=...)` | Create a short-lived JWT (default: `"1h"`) |
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "21st-sdk"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Server-side Python SDK for 21st Agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "21st.dev" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"requests>=2.31.0",
|
|
17
|
+
]
|
|
18
|
+
keywords = [
|
|
19
|
+
"ai",
|
|
20
|
+
"agent",
|
|
21
|
+
"sandbox",
|
|
22
|
+
"21st",
|
|
23
|
+
"21st.dev",
|
|
24
|
+
"sdk",
|
|
25
|
+
"python",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://21st.dev/agents"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.package-dir]
|
|
32
|
+
"" = "src"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
21st_sdk-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: 21st-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Server-side Python SDK for 21st Agents
|
|
5
|
+
Author: 21st.dev
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://21st.dev/agents
|
|
8
|
+
Keywords: ai,agent,sandbox,21st,21st.dev,sdk,python
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: requests>=2.31.0
|
|
12
|
+
|
|
13
|
+
# 21st-sdk Python
|
|
14
|
+
|
|
15
|
+
Server-side Python SDK for [21st Agents](https://21st.dev/agents). Manage sandboxes, threads, and tokens programmatically.
|
|
16
|
+
|
|
17
|
+
Method arguments use Python-style `snake_case`, but SDK responses keep the relay's `camelCase` field names.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install 21st-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import os
|
|
29
|
+
|
|
30
|
+
from twentyfirst_sdk import AgentClient
|
|
31
|
+
|
|
32
|
+
client = AgentClient(api_key=os.environ["API_KEY_21ST"]) # an_sk_...
|
|
33
|
+
|
|
34
|
+
# Create a sandbox for your agent
|
|
35
|
+
sandbox = client.sandboxes.create(agent="my-agent")
|
|
36
|
+
|
|
37
|
+
# Create a thread
|
|
38
|
+
thread = client.threads.create(sandbox_id=sandbox.id, name="Review PR #42")
|
|
39
|
+
|
|
40
|
+
# Generate a short-lived token for browser clients
|
|
41
|
+
token = client.tokens.create(agent="my-agent", expires_in="1h")
|
|
42
|
+
print(token.expiresAt)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### `AgentClient(api_key, base_url=...)`
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
AgentClient(
|
|
51
|
+
api_key="...", # Your an_sk_ API key
|
|
52
|
+
base_url="...", # Optional, default: "https://relay.an.dev"
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `client.sandboxes`
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `create(agent=...)` | Create a new sandbox for an agent |
|
|
61
|
+
| `get(sandbox_id)` | Get sandbox details (status, threads, agent info) |
|
|
62
|
+
| `delete(sandbox_id)` | Delete a sandbox |
|
|
63
|
+
| `exec(sandbox_id, command, ...)` | Run a command in a sandbox |
|
|
64
|
+
| `files.write(sandbox_id, files)` | Write files into a sandbox |
|
|
65
|
+
| `files.read(sandbox_id, path)` | Read a file from a sandbox |
|
|
66
|
+
| `git.clone(sandbox_id, url, ...)` | Clone a repository into a sandbox |
|
|
67
|
+
|
|
68
|
+
### `client.threads`
|
|
69
|
+
|
|
70
|
+
| Method | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| `list(sandbox_id)` | List all threads in a sandbox |
|
|
73
|
+
| `create(sandbox_id, name=...)` | Create a new thread |
|
|
74
|
+
| `get(sandbox_id, thread_id)` | Get thread with messages |
|
|
75
|
+
| `delete(sandbox_id, thread_id)` | Delete a thread |
|
|
76
|
+
| `run(agent, messages, ...)` | Run a thread and return the raw streaming response |
|
|
77
|
+
|
|
78
|
+
### `client.tokens`
|
|
79
|
+
|
|
80
|
+
| Method | Description |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `create(agent=..., user_id=..., expires_in=...)` | Create a short-lived JWT (default: `"1h"`) |
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/21st_sdk.egg-info/PKG-INFO
|
|
4
|
+
src/21st_sdk.egg-info/SOURCES.txt
|
|
5
|
+
src/21st_sdk.egg-info/dependency_links.txt
|
|
6
|
+
src/21st_sdk.egg-info/requires.txt
|
|
7
|
+
src/21st_sdk.egg-info/top_level.txt
|
|
8
|
+
src/twentyfirst_sdk/__init__.py
|
|
9
|
+
src/twentyfirst_sdk/client.py
|
|
10
|
+
src/twentyfirst_sdk/types.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.31.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
twentyfirst_sdk
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .client import DEFAULT_BASE_URL, AgentClient, AgentClientError
|
|
2
|
+
from .types import (
|
|
3
|
+
APIObject,
|
|
4
|
+
ApiError,
|
|
5
|
+
ExecResult,
|
|
6
|
+
FileContent,
|
|
7
|
+
GitCloneResult,
|
|
8
|
+
RunThreadMessage,
|
|
9
|
+
RunThreadMessagePart,
|
|
10
|
+
RunThreadResult,
|
|
11
|
+
Sandbox,
|
|
12
|
+
SandboxDetail,
|
|
13
|
+
Thread,
|
|
14
|
+
ThreadSummary,
|
|
15
|
+
Token,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"APIObject",
|
|
20
|
+
"DEFAULT_BASE_URL",
|
|
21
|
+
"AgentClient",
|
|
22
|
+
"AgentClientError",
|
|
23
|
+
"ApiError",
|
|
24
|
+
"ExecResult",
|
|
25
|
+
"FileContent",
|
|
26
|
+
"GitCloneResult",
|
|
27
|
+
"RunThreadMessage",
|
|
28
|
+
"RunThreadMessagePart",
|
|
29
|
+
"RunThreadResult",
|
|
30
|
+
"Sandbox",
|
|
31
|
+
"SandboxDetail",
|
|
32
|
+
"Thread",
|
|
33
|
+
"ThreadSummary",
|
|
34
|
+
"Token",
|
|
35
|
+
]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Mapping, Optional, Sequence
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .types import APIObject, RunThreadMessage
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
DEFAULT_BASE_URL = "https://relay.an.dev"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _to_api_object(value: Any) -> Any:
|
|
15
|
+
if isinstance(value, dict):
|
|
16
|
+
return APIObject({key: _to_api_object(item) for key, item in value.items()})
|
|
17
|
+
if isinstance(value, list):
|
|
18
|
+
return [_to_api_object(item) for item in value]
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentClientError(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AgentClient:
|
|
27
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
|
|
28
|
+
self.api_key = api_key
|
|
29
|
+
self.base_url = base_url.rstrip("/")
|
|
30
|
+
self._session = requests.Session()
|
|
31
|
+
|
|
32
|
+
self.sandboxes = SandboxesResource(self)
|
|
33
|
+
self.threads = ThreadsResource(self)
|
|
34
|
+
self.tokens = TokensResource(self)
|
|
35
|
+
|
|
36
|
+
def close(self) -> None:
|
|
37
|
+
self._session.close()
|
|
38
|
+
|
|
39
|
+
def __enter__(self) -> "AgentClient":
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
43
|
+
self.close()
|
|
44
|
+
|
|
45
|
+
def _request(
|
|
46
|
+
self,
|
|
47
|
+
path: str,
|
|
48
|
+
*,
|
|
49
|
+
method: str = "GET",
|
|
50
|
+
body: Optional[Dict[str, Any]] = None,
|
|
51
|
+
headers: Optional[Mapping[str, str]] = None,
|
|
52
|
+
stream: bool = False,
|
|
53
|
+
) -> requests.Response:
|
|
54
|
+
request_headers = {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"Authorization": "Bearer %s" % self.api_key,
|
|
57
|
+
}
|
|
58
|
+
if headers:
|
|
59
|
+
request_headers.update(headers)
|
|
60
|
+
|
|
61
|
+
response = self._session.request(
|
|
62
|
+
method,
|
|
63
|
+
"%s%s" % (self.base_url, path),
|
|
64
|
+
json=body,
|
|
65
|
+
headers=request_headers,
|
|
66
|
+
stream=stream,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if not response.ok:
|
|
70
|
+
try:
|
|
71
|
+
payload = response.json()
|
|
72
|
+
except ValueError:
|
|
73
|
+
payload = {}
|
|
74
|
+
|
|
75
|
+
error = payload.get("error") if isinstance(payload, dict) else None
|
|
76
|
+
message = None
|
|
77
|
+
if isinstance(error, dict):
|
|
78
|
+
message = error.get("message")
|
|
79
|
+
|
|
80
|
+
raise AgentClientError(message or "Request failed: %s" % response.status_code)
|
|
81
|
+
|
|
82
|
+
return response
|
|
83
|
+
|
|
84
|
+
def _fetch(
|
|
85
|
+
self,
|
|
86
|
+
path: str,
|
|
87
|
+
*,
|
|
88
|
+
method: str = "GET",
|
|
89
|
+
body: Optional[Dict[str, Any]] = None,
|
|
90
|
+
headers: Optional[Mapping[str, str]] = None,
|
|
91
|
+
) -> Any:
|
|
92
|
+
response = self._request(path, method=method, body=body, headers=headers)
|
|
93
|
+
|
|
94
|
+
if response.status_code == 204:
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
return _to_api_object(response.json())
|
|
98
|
+
|
|
99
|
+
def _get_base_url(self) -> str:
|
|
100
|
+
return self.base_url
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class FilesResource:
|
|
104
|
+
def __init__(self, client: AgentClient):
|
|
105
|
+
self.client = client
|
|
106
|
+
|
|
107
|
+
def write(self, sandbox_id: str, files: Mapping[str, str]) -> Any:
|
|
108
|
+
return self.client._fetch(
|
|
109
|
+
"/v1/sandboxes/%s/files" % sandbox_id,
|
|
110
|
+
method="POST",
|
|
111
|
+
body={"files": dict(files)},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def read(self, sandbox_id: str, path: str) -> Any:
|
|
115
|
+
encoded_path = quote(path, safe="")
|
|
116
|
+
return self.client._fetch(
|
|
117
|
+
"/v1/sandboxes/%s/files?path=%s" % (sandbox_id, encoded_path),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class GitResource:
|
|
122
|
+
def __init__(self, client: AgentClient):
|
|
123
|
+
self.client = client
|
|
124
|
+
|
|
125
|
+
def clone(
|
|
126
|
+
self,
|
|
127
|
+
sandbox_id: str,
|
|
128
|
+
url: str,
|
|
129
|
+
*,
|
|
130
|
+
path: Optional[str] = None,
|
|
131
|
+
token: Optional[str] = None,
|
|
132
|
+
depth: Optional[int] = None,
|
|
133
|
+
) -> Any:
|
|
134
|
+
body: Dict[str, Any] = {"url": url}
|
|
135
|
+
if path:
|
|
136
|
+
body["path"] = path
|
|
137
|
+
if token:
|
|
138
|
+
body["token"] = token
|
|
139
|
+
if depth:
|
|
140
|
+
body["depth"] = depth
|
|
141
|
+
|
|
142
|
+
return self.client._fetch(
|
|
143
|
+
"/v1/sandboxes/%s/git/clone" % sandbox_id,
|
|
144
|
+
method="POST",
|
|
145
|
+
body=body,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class SandboxesResource:
|
|
150
|
+
def __init__(self, client: AgentClient):
|
|
151
|
+
self.client = client
|
|
152
|
+
self.files = FilesResource(client)
|
|
153
|
+
self.git = GitResource(client)
|
|
154
|
+
|
|
155
|
+
def create(
|
|
156
|
+
self,
|
|
157
|
+
agent: str,
|
|
158
|
+
*,
|
|
159
|
+
files: Optional[Mapping[str, str]] = None,
|
|
160
|
+
envs: Optional[Mapping[str, str]] = None,
|
|
161
|
+
setup: Optional[Sequence[str]] = None,
|
|
162
|
+
) -> Any:
|
|
163
|
+
body: Dict[str, Any] = {"agent": agent}
|
|
164
|
+
if files:
|
|
165
|
+
body["files"] = dict(files)
|
|
166
|
+
if envs:
|
|
167
|
+
body["envs"] = dict(envs)
|
|
168
|
+
if setup:
|
|
169
|
+
body["setup"] = list(setup)
|
|
170
|
+
|
|
171
|
+
return self.client._fetch("/v1/sandboxes", method="POST", body=body)
|
|
172
|
+
|
|
173
|
+
def get(self, sandbox_id: str) -> Any:
|
|
174
|
+
return self.client._fetch("/v1/sandboxes/%s" % sandbox_id)
|
|
175
|
+
|
|
176
|
+
def delete(self, sandbox_id: str) -> Any:
|
|
177
|
+
return self.client._fetch("/v1/sandboxes/%s" % sandbox_id, method="DELETE")
|
|
178
|
+
|
|
179
|
+
def exec(
|
|
180
|
+
self,
|
|
181
|
+
sandbox_id: str,
|
|
182
|
+
command: str,
|
|
183
|
+
*,
|
|
184
|
+
cwd: Optional[str] = None,
|
|
185
|
+
envs: Optional[Mapping[str, str]] = None,
|
|
186
|
+
timeout_ms: Optional[int] = None,
|
|
187
|
+
) -> Any:
|
|
188
|
+
body: Dict[str, Any] = {"command": command}
|
|
189
|
+
if cwd:
|
|
190
|
+
body["cwd"] = cwd
|
|
191
|
+
if envs:
|
|
192
|
+
body["envs"] = dict(envs)
|
|
193
|
+
if timeout_ms:
|
|
194
|
+
body["timeoutMs"] = timeout_ms
|
|
195
|
+
|
|
196
|
+
return self.client._fetch(
|
|
197
|
+
"/v1/sandboxes/%s/exec" % sandbox_id,
|
|
198
|
+
method="POST",
|
|
199
|
+
body=body,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ThreadsResource:
|
|
204
|
+
def __init__(self, client: AgentClient):
|
|
205
|
+
self.client = client
|
|
206
|
+
|
|
207
|
+
def list(self, sandbox_id: str) -> Any:
|
|
208
|
+
return self.client._fetch("/v1/sandboxes/%s/threads" % sandbox_id)
|
|
209
|
+
|
|
210
|
+
def create(self, sandbox_id: str, *, name: Optional[str] = None) -> Any:
|
|
211
|
+
body: Dict[str, Any] = {}
|
|
212
|
+
if name is not None:
|
|
213
|
+
body["name"] = name
|
|
214
|
+
|
|
215
|
+
return self.client._fetch(
|
|
216
|
+
"/v1/sandboxes/%s/threads" % sandbox_id,
|
|
217
|
+
method="POST",
|
|
218
|
+
body=body,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def get(self, sandbox_id: str, thread_id: str) -> Any:
|
|
222
|
+
return self.client._fetch(
|
|
223
|
+
"/v1/sandboxes/%s/threads/%s" % (sandbox_id, thread_id),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def delete(self, sandbox_id: str, thread_id: str) -> Any:
|
|
227
|
+
return self.client._fetch(
|
|
228
|
+
"/v1/sandboxes/%s/threads/%s" % (sandbox_id, thread_id),
|
|
229
|
+
method="DELETE",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def run(
|
|
233
|
+
self,
|
|
234
|
+
agent: str,
|
|
235
|
+
messages: Sequence[RunThreadMessage],
|
|
236
|
+
*,
|
|
237
|
+
sandbox_id: Optional[str] = None,
|
|
238
|
+
thread_id: Optional[str] = None,
|
|
239
|
+
name: Optional[str] = None,
|
|
240
|
+
) -> Any:
|
|
241
|
+
if thread_id and not sandbox_id:
|
|
242
|
+
raise AgentClientError("threadId requires sandboxId")
|
|
243
|
+
|
|
244
|
+
if not sandbox_id:
|
|
245
|
+
sandbox_id = self.client.sandboxes.create(agent).id
|
|
246
|
+
|
|
247
|
+
if not thread_id:
|
|
248
|
+
thread_id = self.create(sandbox_id, name=name).id
|
|
249
|
+
|
|
250
|
+
encoded_agent = quote(agent, safe="")
|
|
251
|
+
response = self.client._request(
|
|
252
|
+
"/v1/chat/%s" % encoded_agent,
|
|
253
|
+
method="POST",
|
|
254
|
+
body={
|
|
255
|
+
"messages": list(messages),
|
|
256
|
+
"sandboxId": sandbox_id,
|
|
257
|
+
"threadId": thread_id,
|
|
258
|
+
},
|
|
259
|
+
stream=True,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return APIObject(
|
|
263
|
+
{
|
|
264
|
+
"sandboxId": sandbox_id,
|
|
265
|
+
"threadId": thread_id,
|
|
266
|
+
"response": response,
|
|
267
|
+
"resumeUrl": "%s/v1/chat/%s/%s/stream"
|
|
268
|
+
% (self.client._get_base_url(), encoded_agent, sandbox_id),
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TokensResource:
|
|
274
|
+
def __init__(self, client: AgentClient):
|
|
275
|
+
self.client = client
|
|
276
|
+
|
|
277
|
+
def create(
|
|
278
|
+
self,
|
|
279
|
+
*,
|
|
280
|
+
agent: Optional[str] = None,
|
|
281
|
+
user_id: Optional[str] = None,
|
|
282
|
+
expires_in: str = "1h",
|
|
283
|
+
) -> Any:
|
|
284
|
+
body: Dict[str, Any] = {"expiresIn": expires_in}
|
|
285
|
+
|
|
286
|
+
if agent:
|
|
287
|
+
body["agents"] = [agent]
|
|
288
|
+
|
|
289
|
+
if user_id is not None:
|
|
290
|
+
body["userId"] = user_id
|
|
291
|
+
|
|
292
|
+
return self.client._fetch("/v1/tokens", method="POST", body=body)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping, TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class APIObject(dict):
|
|
7
|
+
"""Dict with attribute-style access, similar to plain JS objects."""
|
|
8
|
+
|
|
9
|
+
def __getattr__(self, name: str) -> Any:
|
|
10
|
+
try:
|
|
11
|
+
return self[name]
|
|
12
|
+
except KeyError as exc:
|
|
13
|
+
raise AttributeError(name) from exc
|
|
14
|
+
|
|
15
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
16
|
+
self[name] = value
|
|
17
|
+
|
|
18
|
+
def __delattr__(self, name: str) -> None:
|
|
19
|
+
try:
|
|
20
|
+
del self[name]
|
|
21
|
+
except KeyError as exc:
|
|
22
|
+
raise AttributeError(name) from exc
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ApiError(TypedDict):
|
|
26
|
+
code: str
|
|
27
|
+
message: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
RunThreadMessagePart = Mapping[str, Any]
|
|
31
|
+
RunThreadMessage = Mapping[str, Any]
|
|
32
|
+
|
|
33
|
+
Sandbox = APIObject
|
|
34
|
+
SandboxDetail = APIObject
|
|
35
|
+
ThreadSummary = APIObject
|
|
36
|
+
RunThreadResult = APIObject
|
|
37
|
+
Thread = APIObject
|
|
38
|
+
Token = APIObject
|
|
39
|
+
FileContent = APIObject
|
|
40
|
+
ExecResult = APIObject
|
|
41
|
+
GitCloneResult = APIObject
|