hexel 0.1.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.
- hexel/__init__.py +4 -0
- hexel/_client.py +36 -0
- hexel/_internal/__init__.py +1 -0
- hexel/_internal/auth.py +52 -0
- hexel/_internal/http.py +48 -0
- hexel/_internal/ws.py +30 -0
- hexel/compute/__init__.py +15 -0
- hexel/compute/_agent.py +45 -0
- hexel/compute/_instance.py +46 -0
- hexel/compute/_sandbox.py +52 -0
- hexel/compute/types.py +444 -0
- hexel-0.1.0.dist-info/METADATA +167 -0
- hexel-0.1.0.dist-info/RECORD +15 -0
- hexel-0.1.0.dist-info/WHEEL +4 -0
- hexel-0.1.0.dist-info/licenses/LICENSE +21 -0
hexel/__init__.py
ADDED
hexel/_client.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from hexel._internal.auth import AuthManager
|
|
3
|
+
from hexel._internal.http import HttpClient
|
|
4
|
+
from hexel.compute import ComputeClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Hexel:
|
|
8
|
+
"""
|
|
9
|
+
Hexel SDK client.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
from hexel import Hexel
|
|
13
|
+
|
|
14
|
+
client = Hexel(api_key="studio_live_xxxx")
|
|
15
|
+
sandbox = client.compute.sandbox.create(tier="standard")
|
|
16
|
+
result = sandbox.execute("print(1 + 1)")
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
api_key: str | None = None,
|
|
23
|
+
client_id: str | None = None,
|
|
24
|
+
client_secret: str | None = None,
|
|
25
|
+
base_url: str = "https://compute.hexelstudio.com",
|
|
26
|
+
sts_url: str = "https://sts.hexelstudio.com",
|
|
27
|
+
timeout: float = 30.0,
|
|
28
|
+
):
|
|
29
|
+
self._auth = AuthManager(
|
|
30
|
+
api_key=api_key,
|
|
31
|
+
client_id=client_id,
|
|
32
|
+
client_secret=client_secret,
|
|
33
|
+
sts_url=sts_url,
|
|
34
|
+
)
|
|
35
|
+
self._http = HttpClient(base_url=base_url, auth=self._auth, timeout=timeout)
|
|
36
|
+
self.compute = ComputeClient(self._http)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
hexel/_internal/auth.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Token management — handles API key and client_credentials auth flows."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import time
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AuthManager:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
*,
|
|
11
|
+
api_key: str | None = None,
|
|
12
|
+
client_id: str | None = None,
|
|
13
|
+
client_secret: str | None = None,
|
|
14
|
+
sts_url: str = "https://sts.hexelstudio.com",
|
|
15
|
+
):
|
|
16
|
+
self._api_key = api_key
|
|
17
|
+
self._client_id = client_id
|
|
18
|
+
self._client_secret = client_secret
|
|
19
|
+
self._sts_url = sts_url
|
|
20
|
+
self._access_token: str | None = None
|
|
21
|
+
self._expires_at: float = 0
|
|
22
|
+
self._http = httpx.Client(timeout=10)
|
|
23
|
+
|
|
24
|
+
if not api_key and not (client_id and client_secret):
|
|
25
|
+
raise ValueError("Provide api_key or (client_id + client_secret)")
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def token(self) -> str:
|
|
29
|
+
if self._access_token and time.time() < self._expires_at - 30:
|
|
30
|
+
return self._access_token
|
|
31
|
+
self._refresh()
|
|
32
|
+
return self._access_token
|
|
33
|
+
|
|
34
|
+
def _refresh(self):
|
|
35
|
+
if self._api_key:
|
|
36
|
+
resp = self._http.post(
|
|
37
|
+
f"{self._sts_url}/token",
|
|
38
|
+
headers={"X-API-Key": self._api_key},
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
resp = self._http.post(
|
|
42
|
+
f"{self._sts_url}/token",
|
|
43
|
+
json={
|
|
44
|
+
"grant_type": "client_credentials",
|
|
45
|
+
"client_id": self._client_id,
|
|
46
|
+
"client_secret": self._client_secret,
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
resp.raise_for_status()
|
|
50
|
+
data = resp.json()
|
|
51
|
+
self._access_token = data["access_token"]
|
|
52
|
+
self._expires_at = time.time() + data.get("expires_in", 900)
|
hexel/_internal/http.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""HTTP request pipeline — retries, auth refresh, error handling."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import time
|
|
4
|
+
import httpx
|
|
5
|
+
from hexel._internal.auth import AuthManager
|
|
6
|
+
|
|
7
|
+
RETRY_STATUS = {429, 500, 502, 503}
|
|
8
|
+
MAX_RETRIES = 3
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HttpClient:
|
|
12
|
+
def __init__(self, *, base_url: str, auth: AuthManager, timeout: float = 30.0):
|
|
13
|
+
self._base_url = base_url.rstrip("/")
|
|
14
|
+
self._auth = auth
|
|
15
|
+
self._client = httpx.Client(timeout=timeout)
|
|
16
|
+
|
|
17
|
+
def request(self, method: str, path: str, **kwargs) -> httpx.Response:
|
|
18
|
+
url = f"{self._base_url}{path}"
|
|
19
|
+
headers = kwargs.pop("headers", {})
|
|
20
|
+
headers["Authorization"] = f"Bearer {self._auth.token}"
|
|
21
|
+
headers.setdefault("Content-Type", "application/json")
|
|
22
|
+
|
|
23
|
+
for attempt in range(MAX_RETRIES + 1):
|
|
24
|
+
resp = self._client.request(method, url, headers=headers, **kwargs)
|
|
25
|
+
if resp.status_code == 401 and attempt == 0:
|
|
26
|
+
self._auth._refresh()
|
|
27
|
+
headers["Authorization"] = f"Bearer {self._auth.token}"
|
|
28
|
+
continue
|
|
29
|
+
if resp.status_code in RETRY_STATUS and attempt < MAX_RETRIES:
|
|
30
|
+
time.sleep(0.5 * (2 ** attempt))
|
|
31
|
+
continue
|
|
32
|
+
return resp
|
|
33
|
+
return resp
|
|
34
|
+
|
|
35
|
+
def get(self, path, **kw) -> httpx.Response:
|
|
36
|
+
return self.request("GET", path, **kw)
|
|
37
|
+
|
|
38
|
+
def post(self, path, **kw) -> httpx.Response:
|
|
39
|
+
return self.request("POST", path, **kw)
|
|
40
|
+
|
|
41
|
+
def put(self, path, **kw) -> httpx.Response:
|
|
42
|
+
return self.request("PUT", path, **kw)
|
|
43
|
+
|
|
44
|
+
def delete(self, path, **kw) -> httpx.Response:
|
|
45
|
+
return self.request("DELETE", path, **kw)
|
|
46
|
+
|
|
47
|
+
def patch(self, path, **kw) -> httpx.Response:
|
|
48
|
+
return self.request("PATCH", path, **kw)
|
hexel/_internal/ws.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""WebSocket client for sandbox execution."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import json
|
|
4
|
+
from websockets.sync.client import connect
|
|
5
|
+
from hexel._internal.auth import AuthManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WSConnection:
|
|
9
|
+
"""Persistent WebSocket connection to a sandbox's agentd."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, url: str, auth: AuthManager):
|
|
12
|
+
self._ws = connect(url, additional_headers={"Authorization": f"Bearer {auth.token}"})
|
|
13
|
+
msg = json.loads(self._ws.recv())
|
|
14
|
+
self.context_id = msg.get("data", "")
|
|
15
|
+
|
|
16
|
+
def send(self, msg: dict) -> list[dict]:
|
|
17
|
+
self._ws.send(json.dumps(msg))
|
|
18
|
+
results = []
|
|
19
|
+
while True:
|
|
20
|
+
data = json.loads(self._ws.recv())
|
|
21
|
+
results.append(data)
|
|
22
|
+
if data.get("type") in ("done", "execution_complete", "error"):
|
|
23
|
+
break
|
|
24
|
+
return results
|
|
25
|
+
|
|
26
|
+
def close(self):
|
|
27
|
+
try:
|
|
28
|
+
self._ws.close()
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Generated by hexel-sdk-generator. Do not edit manually."""
|
|
2
|
+
from hexel.compute._sandbox import SandboxClient
|
|
3
|
+
from hexel.compute._agent import AgentClient
|
|
4
|
+
from hexel.compute._instance import InstanceClient
|
|
5
|
+
|
|
6
|
+
__all__ = ["ComputeClient", "SandboxClient", "AgentClient", "InstanceClient"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ComputeClient:
|
|
10
|
+
"""Hexel Compute — deploy agents and run sandboxes."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, http):
|
|
13
|
+
self.sandbox = SandboxClient(http)
|
|
14
|
+
self.agent = AgentClient(http)
|
|
15
|
+
self.instance = InstanceClient(http)
|
hexel/compute/_agent.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Generated by hexel-sdk-generator. Do not edit manually."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from hexel._internal.http import HttpClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentClient:
|
|
7
|
+
"""Agent registry — register and manage agent images"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, http: HttpClient):
|
|
10
|
+
self._http = http
|
|
11
|
+
|
|
12
|
+
def register(self, **kwargs) -> dict:
|
|
13
|
+
"""Register a new agent in the catalog"""
|
|
14
|
+
resp = self._http.post("/registry/v1/agents", json=kwargs)
|
|
15
|
+
resp.raise_for_status()
|
|
16
|
+
return resp.json()
|
|
17
|
+
|
|
18
|
+
def list(self) -> dict:
|
|
19
|
+
"""List agents with optional filters"""
|
|
20
|
+
resp = self._http.get("/registry/v1/agents")
|
|
21
|
+
resp.raise_for_status()
|
|
22
|
+
return resp.json()
|
|
23
|
+
|
|
24
|
+
def get(self, id: str) -> dict:
|
|
25
|
+
"""Get agent by ID"""
|
|
26
|
+
resp = self._http.get("/registry/v1/agents/{id}".replace("{id}", id))
|
|
27
|
+
resp.raise_for_status()
|
|
28
|
+
return resp.json()
|
|
29
|
+
|
|
30
|
+
def update(self, id: str, **kwargs) -> dict:
|
|
31
|
+
"""Update agent fields"""
|
|
32
|
+
resp = self._http.put("/registry/v1/agents/{id}".replace("{id}", id), json=kwargs)
|
|
33
|
+
resp.raise_for_status()
|
|
34
|
+
return resp.json()
|
|
35
|
+
|
|
36
|
+
def delete(self, id: str) -> None:
|
|
37
|
+
"""Delete agent by ID"""
|
|
38
|
+
resp = self._http.delete("/registry/v1/agents/{id}".replace("{id}", id))
|
|
39
|
+
resp.raise_for_status()
|
|
40
|
+
|
|
41
|
+
def search(self) -> dict:
|
|
42
|
+
"""Find agents matching given capabilities"""
|
|
43
|
+
resp = self._http.get("/registry/v1/agents/search")
|
|
44
|
+
resp.raise_for_status()
|
|
45
|
+
return resp.json()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Generated by hexel-sdk-generator. Do not edit manually."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from hexel._internal.http import HttpClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InstanceClient:
|
|
7
|
+
"""Agent deployments — deploy agents and manage instances"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, http: HttpClient):
|
|
10
|
+
self._http = http
|
|
11
|
+
|
|
12
|
+
def deploy(self, agent_id: str) -> dict:
|
|
13
|
+
"""Creates a new deployment from a registered agent. Returns a deployment_id to use for all subsequent operations."""
|
|
14
|
+
resp = self._http.post("/compute/v1/agents/{agent_id}/deploy".replace("{agent_id}", agent_id))
|
|
15
|
+
resp.raise_for_status()
|
|
16
|
+
return resp.json()
|
|
17
|
+
|
|
18
|
+
def list(self) -> dict:
|
|
19
|
+
"""List all deployments"""
|
|
20
|
+
resp = self._http.get("/compute/v1/deployments")
|
|
21
|
+
resp.raise_for_status()
|
|
22
|
+
data = resp.json()
|
|
23
|
+
return data if isinstance(data, list) else data.get("agents", data.get("items", []))
|
|
24
|
+
|
|
25
|
+
def get(self, deployment_id: str) -> dict:
|
|
26
|
+
"""Get deployment by ID"""
|
|
27
|
+
resp = self._http.get("/compute/v1/deployments/{deployment_id}".replace("{deployment_id}", deployment_id))
|
|
28
|
+
resp.raise_for_status()
|
|
29
|
+
return resp.json()
|
|
30
|
+
|
|
31
|
+
def delete(self, deployment_id: str) -> None:
|
|
32
|
+
"""Delete a deployment"""
|
|
33
|
+
resp = self._http.delete("/compute/v1/deployments/{deployment_id}".replace("{deployment_id}", deployment_id))
|
|
34
|
+
resp.raise_for_status()
|
|
35
|
+
|
|
36
|
+
def stop(self, deployment_id: str) -> dict:
|
|
37
|
+
"""Stop a deployment"""
|
|
38
|
+
resp = self._http.post("/compute/v1/deployments/{deployment_id}/stop".replace("{deployment_id}", deployment_id))
|
|
39
|
+
resp.raise_for_status()
|
|
40
|
+
return resp.json()
|
|
41
|
+
|
|
42
|
+
def redeploy(self, deployment_id: str) -> dict:
|
|
43
|
+
"""Redeploy (stop + start)"""
|
|
44
|
+
resp = self._http.post("/compute/v1/deployments/{deployment_id}/redeploy".replace("{deployment_id}", deployment_id))
|
|
45
|
+
resp.raise_for_status()
|
|
46
|
+
return resp.json()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Generated by hexel-sdk-generator. Do not edit manually."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from hexel._internal.http import HttpClient
|
|
4
|
+
from hexel._internal.ws import WSConnection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SandboxClient:
|
|
8
|
+
"""Isolated execution environments"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, http: HttpClient):
|
|
11
|
+
self._http = http
|
|
12
|
+
|
|
13
|
+
def create(self, **kwargs) -> dict:
|
|
14
|
+
"""Allocates a VM from the pool or creates a new one for the requesting org"""
|
|
15
|
+
resp = self._http.post("/compute/v1/vms/allocate", json=kwargs)
|
|
16
|
+
resp.raise_for_status()
|
|
17
|
+
return resp.json()
|
|
18
|
+
|
|
19
|
+
def list(self) -> dict:
|
|
20
|
+
"""Returns VMs scoped to the caller's org"""
|
|
21
|
+
resp = self._http.get("/compute/v1/vms")
|
|
22
|
+
resp.raise_for_status()
|
|
23
|
+
data = resp.json()
|
|
24
|
+
return data if isinstance(data, list) else data.get("agents", data.get("items", []))
|
|
25
|
+
|
|
26
|
+
def get(self, id: str) -> dict:
|
|
27
|
+
"""Get Agent VM by ID"""
|
|
28
|
+
resp = self._http.get("/compute/v1/vms/{id}".replace("{id}", id))
|
|
29
|
+
resp.raise_for_status()
|
|
30
|
+
return resp.json()
|
|
31
|
+
|
|
32
|
+
def delete(self, id: str) -> None:
|
|
33
|
+
"""Delete Agent VM"""
|
|
34
|
+
resp = self._http.delete("/compute/v1/vms/{id}".replace("{id}", id))
|
|
35
|
+
resp.raise_for_status()
|
|
36
|
+
|
|
37
|
+
def release(self, id: str, **kwargs) -> dict:
|
|
38
|
+
"""Release Agent VM"""
|
|
39
|
+
resp = self._http.post("/compute/v1/vms/{id}/release".replace("{id}", id), json=kwargs)
|
|
40
|
+
resp.raise_for_status()
|
|
41
|
+
return resp.json()
|
|
42
|
+
|
|
43
|
+
def renew(self, id: str, **kwargs) -> dict:
|
|
44
|
+
"""Renew Agent VM TTL"""
|
|
45
|
+
resp = self._http.post("/compute/v1/vms/{id}/renew".replace("{id}", id), json=kwargs)
|
|
46
|
+
resp.raise_for_status()
|
|
47
|
+
return resp.json()
|
|
48
|
+
|
|
49
|
+
def execute(self, id: str) -> None:
|
|
50
|
+
"""Runs code or shell commands in an allocated sandbox VM"""
|
|
51
|
+
resp = self._http.post("/compute/v1/vms/{id}/execute".replace("{id}", id))
|
|
52
|
+
resp.raise_for_status()
|
hexel/compute/types.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""Generated by hexel-sdk-generator. Do not edit manually."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class OAuthClient:
|
|
8
|
+
"""iamclient.OAuthClient"""
|
|
9
|
+
client_id: str | None = None
|
|
10
|
+
created_at: str | None = None
|
|
11
|
+
is_active: bool | None = None
|
|
12
|
+
name: str | None = None
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_dict(cls, d: dict) -> "OAuthClient":
|
|
15
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class OAuthClientResponse:
|
|
19
|
+
"""iamclient.OAuthClientResponse"""
|
|
20
|
+
client_id: str | None = None
|
|
21
|
+
client_secret: str | None = None
|
|
22
|
+
name: str | None = None
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_dict(cls, d: dict) -> "OAuthClientResponse":
|
|
25
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class InstanceInfo:
|
|
29
|
+
"""models.Agent"""
|
|
30
|
+
allowed_org_ids: list[str] | None = None
|
|
31
|
+
author: str | None = None
|
|
32
|
+
capabilities: list[str] | None = None
|
|
33
|
+
created_at: str | None = None
|
|
34
|
+
description: str | None = None
|
|
35
|
+
endpoint: str | None = None
|
|
36
|
+
id: str | None = None
|
|
37
|
+
image: str | None = None
|
|
38
|
+
image_pull_secret: models.ImagePullSecret | None = None
|
|
39
|
+
manifest: models.AgentManifest | None = None
|
|
40
|
+
metadata: dict[str, string] | None = None
|
|
41
|
+
name: str | None = None
|
|
42
|
+
org_id: str | None = None
|
|
43
|
+
public_key: str | None = None
|
|
44
|
+
state: str | None = None
|
|
45
|
+
updated_at: str | None = None
|
|
46
|
+
version: str | None = None
|
|
47
|
+
visibility: str | None = None
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_dict(cls, d: dict) -> "InstanceInfo":
|
|
50
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class SandboxInfo:
|
|
54
|
+
"""models.AgentVM"""
|
|
55
|
+
allocated_at: str | None = None
|
|
56
|
+
created_at: str | None = None
|
|
57
|
+
expires_at: str | None = None
|
|
58
|
+
id: str | None = None
|
|
59
|
+
metadata: dict[str, string] | None = None
|
|
60
|
+
released_at: str | None = None
|
|
61
|
+
skills: list[str] | None = None
|
|
62
|
+
state: str | None = None
|
|
63
|
+
tier: str | None = None
|
|
64
|
+
token: str | None = None
|
|
65
|
+
ttl_seconds: int | None = None
|
|
66
|
+
@classmethod
|
|
67
|
+
def from_dict(cls, d: dict) -> "SandboxInfo":
|
|
68
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class CreateSandboxRequest:
|
|
72
|
+
"""models.AllocateRequest"""
|
|
73
|
+
env: dict[str, string] | None = None
|
|
74
|
+
metadata: dict[str, string] | None = None
|
|
75
|
+
org_id: str | None = None
|
|
76
|
+
secure_runtime: str | None = None
|
|
77
|
+
skills: list[str] | None = None
|
|
78
|
+
tier: str
|
|
79
|
+
ttl_seconds: int | None = None
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_dict(cls, d: dict) -> "CreateSandboxRequest":
|
|
82
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class SandboxResponse:
|
|
86
|
+
"""models.AllocateResponse"""
|
|
87
|
+
endpoint: str | None = None
|
|
88
|
+
expires_at: str | None = None
|
|
89
|
+
skills_loaded: list[str] | None = None
|
|
90
|
+
state: str | None = None
|
|
91
|
+
tier: str | None = None
|
|
92
|
+
token: str | None = None
|
|
93
|
+
vm_id: str | None = None
|
|
94
|
+
ws_url: str | None = None
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_dict(cls, d: dict) -> "SandboxResponse":
|
|
97
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class CreateSkillRequest:
|
|
101
|
+
"""models.CreateSkillRequest"""
|
|
102
|
+
content: str
|
|
103
|
+
description: str | None = None
|
|
104
|
+
name: str
|
|
105
|
+
resources: dict[str, string] | None = None
|
|
106
|
+
@classmethod
|
|
107
|
+
def from_dict(cls, d: dict) -> "CreateSkillRequest":
|
|
108
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class ErrorResponse:
|
|
112
|
+
"""models.ErrorResponse"""
|
|
113
|
+
code: int | None = None
|
|
114
|
+
message: str | None = None
|
|
115
|
+
@classmethod
|
|
116
|
+
def from_dict(cls, d: dict) -> "ErrorResponse":
|
|
117
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class PoolConfig:
|
|
121
|
+
"""models.PoolConfig"""
|
|
122
|
+
default_skills: list[str] | None = None
|
|
123
|
+
default_tier: str | None = None
|
|
124
|
+
idle_ttl_seconds: int | None = None
|
|
125
|
+
max_total: int | None = None
|
|
126
|
+
max_vm_ttl_seconds: int | None = None
|
|
127
|
+
max_warm: int | None = None
|
|
128
|
+
min_warm: int | None = None
|
|
129
|
+
recycle: bool | None = None
|
|
130
|
+
scale_up_threshold: float | None = None
|
|
131
|
+
@classmethod
|
|
132
|
+
def from_dict(cls, d: dict) -> "PoolConfig":
|
|
133
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
134
|
+
|
|
135
|
+
@dataclass
|
|
136
|
+
class PoolStatus:
|
|
137
|
+
"""models.PoolStatus"""
|
|
138
|
+
allocated: int | None = None
|
|
139
|
+
by_tier: dict[str, integer] | None = None
|
|
140
|
+
total: int | None = None
|
|
141
|
+
warm: int | None = None
|
|
142
|
+
@classmethod
|
|
143
|
+
def from_dict(cls, d: dict) -> "PoolStatus":
|
|
144
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class ReleaseRequest:
|
|
148
|
+
"""models.ReleaseRequest"""
|
|
149
|
+
recycle: bool | None = None
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_dict(cls, d: dict) -> "ReleaseRequest":
|
|
152
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
153
|
+
|
|
154
|
+
@dataclass
|
|
155
|
+
class RenewRequest:
|
|
156
|
+
"""models.RenewRequest"""
|
|
157
|
+
ttl_seconds: int
|
|
158
|
+
@classmethod
|
|
159
|
+
def from_dict(cls, d: dict) -> "RenewRequest":
|
|
160
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class Skill:
|
|
164
|
+
"""models.Skill"""
|
|
165
|
+
content: str | None = None
|
|
166
|
+
content_hash: str | None = None
|
|
167
|
+
created_at: str | None = None
|
|
168
|
+
description: str | None = None
|
|
169
|
+
name: str | None = None
|
|
170
|
+
resources: dict[str, string] | None = None
|
|
171
|
+
updated_at: str | None = None
|
|
172
|
+
@classmethod
|
|
173
|
+
def from_dict(cls, d: dict) -> "Skill":
|
|
174
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class UsageRecord:
|
|
178
|
+
"""models.UsageRecord"""
|
|
179
|
+
duration_seconds: int | None = None
|
|
180
|
+
ended_at: str | None = None
|
|
181
|
+
reason: str | None = None
|
|
182
|
+
started_at: str | None = None
|
|
183
|
+
tier: str | None = None
|
|
184
|
+
vm_id: str | None = None
|
|
185
|
+
@classmethod
|
|
186
|
+
def from_dict(cls, d: dict) -> "UsageRecord":
|
|
187
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
188
|
+
|
|
189
|
+
@dataclass
|
|
190
|
+
class UsageSummary:
|
|
191
|
+
"""models.UsageSummary"""
|
|
192
|
+
by_tier: dict[str, integer] | None = None
|
|
193
|
+
period_end: str | None = None
|
|
194
|
+
period_start: str | None = None
|
|
195
|
+
total_seconds: int | None = None
|
|
196
|
+
vm_count: int | None = None
|
|
197
|
+
@classmethod
|
|
198
|
+
def from_dict(cls, d: dict) -> "UsageSummary":
|
|
199
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
200
|
+
|
|
201
|
+
@dataclass
|
|
202
|
+
class AgentManifest:
|
|
203
|
+
"""models.AgentManifest"""
|
|
204
|
+
authentication: dict | None = None
|
|
205
|
+
capabilities: dict | None = None
|
|
206
|
+
description: str | None = None
|
|
207
|
+
input_schema: dict | None = None
|
|
208
|
+
name: str | None = None
|
|
209
|
+
output_schema: dict | None = None
|
|
210
|
+
runtime: dict | None = None
|
|
211
|
+
skills: list[models.ManifestSkill] | None = None
|
|
212
|
+
url: str | None = None
|
|
213
|
+
version: str | None = None
|
|
214
|
+
@classmethod
|
|
215
|
+
def from_dict(cls, d: dict) -> "AgentManifest":
|
|
216
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
217
|
+
|
|
218
|
+
@dataclass
|
|
219
|
+
class AgentResponse:
|
|
220
|
+
"""models.AgentResponse"""
|
|
221
|
+
agent: models.Agent | None = None
|
|
222
|
+
@classmethod
|
|
223
|
+
def from_dict(cls, d: dict) -> "AgentResponse":
|
|
224
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
225
|
+
|
|
226
|
+
@dataclass
|
|
227
|
+
class AgentsListResponse:
|
|
228
|
+
"""models.AgentsListResponse"""
|
|
229
|
+
agents: list[models.Agent] | None = None
|
|
230
|
+
total: int | None = None
|
|
231
|
+
@classmethod
|
|
232
|
+
def from_dict(cls, d: dict) -> "AgentsListResponse":
|
|
233
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
234
|
+
|
|
235
|
+
@dataclass
|
|
236
|
+
class CreateAgentRequest:
|
|
237
|
+
"""models.CreateAgentRequest"""
|
|
238
|
+
author: str | None = None
|
|
239
|
+
capabilities: list[str] | None = None
|
|
240
|
+
description: str | None = None
|
|
241
|
+
endpoint: str | None = None
|
|
242
|
+
image: str
|
|
243
|
+
image_pull_secret: models.ImagePullSecret | None = None
|
|
244
|
+
manifest: models.AgentManifest | None = None
|
|
245
|
+
metadata: dict[str, string] | None = None
|
|
246
|
+
name: str
|
|
247
|
+
org_id: str
|
|
248
|
+
version: str | None = None
|
|
249
|
+
@classmethod
|
|
250
|
+
def from_dict(cls, d: dict) -> "CreateAgentRequest":
|
|
251
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
252
|
+
|
|
253
|
+
@dataclass
|
|
254
|
+
class CreateToolRequest:
|
|
255
|
+
"""models.CreateToolRequest"""
|
|
256
|
+
category: str | None = None
|
|
257
|
+
description: str | None = None
|
|
258
|
+
id: str
|
|
259
|
+
name: str
|
|
260
|
+
@classmethod
|
|
261
|
+
def from_dict(cls, d: dict) -> "CreateToolRequest":
|
|
262
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
263
|
+
|
|
264
|
+
@dataclass
|
|
265
|
+
class ImagePullSecret:
|
|
266
|
+
"""models.ImagePullSecret"""
|
|
267
|
+
password: str | None = None
|
|
268
|
+
registry: str | None = None
|
|
269
|
+
username: str | None = None
|
|
270
|
+
@classmethod
|
|
271
|
+
def from_dict(cls, d: dict) -> "ImagePullSecret":
|
|
272
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
273
|
+
|
|
274
|
+
@dataclass
|
|
275
|
+
class ManifestSkill:
|
|
276
|
+
"""models.ManifestSkill"""
|
|
277
|
+
id: str | None = None
|
|
278
|
+
inputModes: list[str] | None = None
|
|
279
|
+
outputModes: list[str] | None = None
|
|
280
|
+
@classmethod
|
|
281
|
+
def from_dict(cls, d: dict) -> "ManifestSkill":
|
|
282
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
283
|
+
|
|
284
|
+
@dataclass
|
|
285
|
+
class Tool:
|
|
286
|
+
"""models.Tool"""
|
|
287
|
+
category: str | None = None
|
|
288
|
+
created_at: str | None = None
|
|
289
|
+
description: str | None = None
|
|
290
|
+
id: str | None = None
|
|
291
|
+
name: str | None = None
|
|
292
|
+
@classmethod
|
|
293
|
+
def from_dict(cls, d: dict) -> "Tool":
|
|
294
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
295
|
+
|
|
296
|
+
@dataclass
|
|
297
|
+
class ToolResponse:
|
|
298
|
+
"""models.ToolResponse"""
|
|
299
|
+
tool: models.Tool | None = None
|
|
300
|
+
@classmethod
|
|
301
|
+
def from_dict(cls, d: dict) -> "ToolResponse":
|
|
302
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
303
|
+
|
|
304
|
+
@dataclass
|
|
305
|
+
class ToolsListResponse:
|
|
306
|
+
"""models.ToolsListResponse"""
|
|
307
|
+
tools: list[models.Tool] | None = None
|
|
308
|
+
total: int | None = None
|
|
309
|
+
@classmethod
|
|
310
|
+
def from_dict(cls, d: dict) -> "ToolsListResponse":
|
|
311
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
312
|
+
|
|
313
|
+
@dataclass
|
|
314
|
+
class UpdateAgentRequest:
|
|
315
|
+
"""models.UpdateAgentRequest"""
|
|
316
|
+
capabilities: list[str] | None = None
|
|
317
|
+
description: str | None = None
|
|
318
|
+
endpoint: str | None = None
|
|
319
|
+
image: str | None = None
|
|
320
|
+
manifest: models.AgentManifest | None = None
|
|
321
|
+
metadata: dict[str, string] | None = None
|
|
322
|
+
name: str | None = None
|
|
323
|
+
public_key: str | None = None
|
|
324
|
+
state: str | None = None
|
|
325
|
+
version: str | None = None
|
|
326
|
+
@classmethod
|
|
327
|
+
def from_dict(cls, d: dict) -> "UpdateAgentRequest":
|
|
328
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
329
|
+
|
|
330
|
+
@dataclass
|
|
331
|
+
class CodeContext:
|
|
332
|
+
"""models.CodeContext"""
|
|
333
|
+
created_at: str | None = None
|
|
334
|
+
id: str | None = None
|
|
335
|
+
kernel_id: str | None = None
|
|
336
|
+
language: str | None = None
|
|
337
|
+
session_id: str | None = None
|
|
338
|
+
@classmethod
|
|
339
|
+
def from_dict(cls, d: dict) -> "CodeContext":
|
|
340
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
341
|
+
|
|
342
|
+
@dataclass
|
|
343
|
+
class CommandStatus:
|
|
344
|
+
"""models.CommandStatus"""
|
|
345
|
+
exit_code: int | None = None
|
|
346
|
+
finished_at: str | None = None
|
|
347
|
+
id: str | None = None
|
|
348
|
+
running: bool | None = None
|
|
349
|
+
started_at: str | None = None
|
|
350
|
+
@classmethod
|
|
351
|
+
def from_dict(cls, d: dict) -> "CommandStatus":
|
|
352
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
353
|
+
|
|
354
|
+
@dataclass
|
|
355
|
+
class CreateContextRequest:
|
|
356
|
+
"""models.CreateContextRequest"""
|
|
357
|
+
language: str
|
|
358
|
+
session_id: str
|
|
359
|
+
@classmethod
|
|
360
|
+
def from_dict(cls, d: dict) -> "CreateContextRequest":
|
|
361
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
362
|
+
|
|
363
|
+
@dataclass
|
|
364
|
+
class ExecuteCodeRequest:
|
|
365
|
+
"""models.ExecuteCodeRequest"""
|
|
366
|
+
code: str
|
|
367
|
+
context_id: str
|
|
368
|
+
@classmethod
|
|
369
|
+
def from_dict(cls, d: dict) -> "ExecuteCodeRequest":
|
|
370
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
371
|
+
|
|
372
|
+
@dataclass
|
|
373
|
+
class ExecuteRequest:
|
|
374
|
+
"""models.ExecuteRequest"""
|
|
375
|
+
@classmethod
|
|
376
|
+
def from_dict(cls, d: dict) -> "ExecuteRequest":
|
|
377
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
378
|
+
|
|
379
|
+
@dataclass
|
|
380
|
+
class FileInfo:
|
|
381
|
+
"""models.FileInfo"""
|
|
382
|
+
group: str | None = None
|
|
383
|
+
is_dir: bool | None = None
|
|
384
|
+
mod_time: str | None = None
|
|
385
|
+
mode: str | None = None
|
|
386
|
+
owner: str | None = None
|
|
387
|
+
path: str | None = None
|
|
388
|
+
size: int | None = None
|
|
389
|
+
@classmethod
|
|
390
|
+
def from_dict(cls, d: dict) -> "FileInfo":
|
|
391
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
392
|
+
|
|
393
|
+
@dataclass
|
|
394
|
+
class LoadSkillsRequest:
|
|
395
|
+
"""models.LoadSkillsRequest"""
|
|
396
|
+
skills: list[str]
|
|
397
|
+
@classmethod
|
|
398
|
+
def from_dict(cls, d: dict) -> "LoadSkillsRequest":
|
|
399
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
400
|
+
|
|
401
|
+
@dataclass
|
|
402
|
+
class MetricsSnapshot:
|
|
403
|
+
"""models.MetricsSnapshot"""
|
|
404
|
+
cpu_percent: float | None = None
|
|
405
|
+
mem_total_bytes: int | None = None
|
|
406
|
+
mem_used_bytes: int | None = None
|
|
407
|
+
tokens_total: int | None = None
|
|
408
|
+
@classmethod
|
|
409
|
+
def from_dict(cls, d: dict) -> "MetricsSnapshot":
|
|
410
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
411
|
+
|
|
412
|
+
@dataclass
|
|
413
|
+
class RunCommandRequest:
|
|
414
|
+
"""models.RunCommandRequest"""
|
|
415
|
+
background: bool | None = None
|
|
416
|
+
command: str
|
|
417
|
+
cwd: str | None = None
|
|
418
|
+
envs: dict[str, string] | None = None
|
|
419
|
+
gid: int | None = None
|
|
420
|
+
timeout_ms: int | None = None
|
|
421
|
+
uid: int | None = None
|
|
422
|
+
@classmethod
|
|
423
|
+
def from_dict(cls, d: dict) -> "RunCommandRequest":
|
|
424
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
425
|
+
|
|
426
|
+
@dataclass
|
|
427
|
+
class SSEEvent:
|
|
428
|
+
"""models.SSEEvent"""
|
|
429
|
+
data: str | None = None
|
|
430
|
+
event: str | None = None
|
|
431
|
+
@classmethod
|
|
432
|
+
def from_dict(cls, d: dict) -> "SSEEvent":
|
|
433
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
434
|
+
|
|
435
|
+
@dataclass
|
|
436
|
+
class SkillInfo:
|
|
437
|
+
"""models.SkillInfo"""
|
|
438
|
+
loaded: bool | None = None
|
|
439
|
+
name: str | None = None
|
|
440
|
+
path: str | None = None
|
|
441
|
+
@classmethod
|
|
442
|
+
def from_dict(cls, d: dict) -> "SkillInfo":
|
|
443
|
+
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
|
|
444
|
+
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hexel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hexel Studio SDK — deploy and run AI agents
|
|
5
|
+
Project-URL: Homepage, https://hexelstudio.com
|
|
6
|
+
Project-URL: Documentation, https://docs.hexelstudio.com
|
|
7
|
+
Project-URL: Repository, https://github.com/hexelstudio/python-sdk
|
|
8
|
+
Project-URL: Changelog, https://docs.hexelstudio.com/changelog
|
|
9
|
+
Author-email: Hexel Studio <support@hexelstudio.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,code-execution,compute,hexel,sandbox
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: websockets>=12.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: jinja2; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Hexel Python SDK
|
|
32
|
+
|
|
33
|
+
The official Python SDK for [Hexel Studio](https://hexelstudio.com) — build, deploy, and run AI agents.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install hexel
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from hexel import Hexel
|
|
45
|
+
|
|
46
|
+
client = Hexel(api_key="your_api_key")
|
|
47
|
+
|
|
48
|
+
# Create a sandbox and execute code
|
|
49
|
+
sandbox = client.compute.sandbox.create(tier="standard")
|
|
50
|
+
print(sandbox) # {"vm_id": "...", "state": "allocated", ...}
|
|
51
|
+
|
|
52
|
+
# Register an agent
|
|
53
|
+
agent = client.compute.agent.register(
|
|
54
|
+
name="my-agent",
|
|
55
|
+
image="ghcr.io/org/agent:v1",
|
|
56
|
+
capabilities=["chat", "streaming"],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Deploy an agent
|
|
60
|
+
instance = client.compute.instance.deploy(agent["id"], env={"MODEL": "gpt-4"})
|
|
61
|
+
print(instance["endpoint"]) # https://zeal-isle-a620.compute.hexelstudio.com
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Authentication
|
|
65
|
+
|
|
66
|
+
Two authentication methods are supported:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# API Key (recommended for development)
|
|
70
|
+
client = Hexel(api_key="studio_live_xxxx")
|
|
71
|
+
|
|
72
|
+
# OAuth Client Credentials (recommended for production)
|
|
73
|
+
client = Hexel(client_id="your_client_id", client_secret="your_client_secret")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Both methods exchange credentials for a short-lived STS token automatically. Tokens are cached and refreshed transparently.
|
|
77
|
+
|
|
78
|
+
## Sandboxes
|
|
79
|
+
|
|
80
|
+
Sandboxes are isolated execution environments allocated instantly.
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# Create
|
|
84
|
+
sandbox = client.compute.sandbox.create(tier="standard")
|
|
85
|
+
|
|
86
|
+
# Execute code (HTTP)
|
|
87
|
+
result = client.compute.sandbox.execute(sandbox["vm_id"], code="print(1+1)", language="python")
|
|
88
|
+
|
|
89
|
+
# List
|
|
90
|
+
sandboxes = client.compute.sandbox.list()
|
|
91
|
+
|
|
92
|
+
# Renew TTL
|
|
93
|
+
client.compute.sandbox.renew(sandbox["vm_id"], ttl_seconds=3600)
|
|
94
|
+
|
|
95
|
+
# Release
|
|
96
|
+
client.compute.sandbox.release(sandbox["vm_id"], recycle=True)
|
|
97
|
+
|
|
98
|
+
# Delete
|
|
99
|
+
client.compute.sandbox.delete(sandbox["vm_id"])
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Agents
|
|
103
|
+
|
|
104
|
+
Agents are Docker images registered in the Hexel registry.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# Register
|
|
108
|
+
agent = client.compute.agent.register(
|
|
109
|
+
name="fraud-detector",
|
|
110
|
+
image="ghcr.io/org/fraud-agent:v1",
|
|
111
|
+
capabilities=["fraud-detection"],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# List
|
|
115
|
+
agents = client.compute.agent.list()
|
|
116
|
+
|
|
117
|
+
# Get
|
|
118
|
+
agent = client.compute.agent.get("agent-id")
|
|
119
|
+
|
|
120
|
+
# Search
|
|
121
|
+
results = client.compute.agent.search(q="fraud")
|
|
122
|
+
|
|
123
|
+
# Delete
|
|
124
|
+
client.compute.agent.delete("agent-id")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Instances
|
|
128
|
+
|
|
129
|
+
Instances are running deployments of agents with permanent endpoints.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# Deploy
|
|
133
|
+
instance = client.compute.instance.deploy("agent-id", env={"MODEL": "gpt-4"})
|
|
134
|
+
|
|
135
|
+
# List
|
|
136
|
+
instances = client.compute.instance.list()
|
|
137
|
+
|
|
138
|
+
# Get
|
|
139
|
+
instance = client.compute.instance.get("deployment-id")
|
|
140
|
+
|
|
141
|
+
# Stop
|
|
142
|
+
client.compute.instance.stop("deployment-id")
|
|
143
|
+
|
|
144
|
+
# Redeploy
|
|
145
|
+
client.compute.instance.redeploy("deployment-id")
|
|
146
|
+
|
|
147
|
+
# Delete
|
|
148
|
+
client.compute.instance.delete("deployment-id")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
client = Hexel(
|
|
155
|
+
api_key="...",
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Requirements
|
|
160
|
+
|
|
161
|
+
- Python 3.10+
|
|
162
|
+
- `httpx` for HTTP
|
|
163
|
+
- `websockets` for WebSocket connections
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
hexel/__init__.py,sha256=pqLIT-XJ5MV5_lwu0uRjq4k0rN46hIThTAj_VT_kJ3o,75
|
|
2
|
+
hexel/_client.py,sha256=0v7U4uKDvbqj0Xuj0NcIK-gSckjNBQixLxNN_rbNqMg,1048
|
|
3
|
+
hexel/_internal/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
4
|
+
hexel/_internal/auth.py,sha256=OEIXIcLOYY5TpbNYN5d0MDOti0yi0Zx0iOYaR3w5bIw,1690
|
|
5
|
+
hexel/_internal/http.py,sha256=asf6z0mNSmIO8FN4ogEpYE-ZHvSP7LztdWpDAq9PTkw,1755
|
|
6
|
+
hexel/_internal/ws.py,sha256=e7k21FbcJ-8ntSwLfCC8trJBYk-Fp6AbKPOpts_pOak,938
|
|
7
|
+
hexel/compute/__init__.py,sha256=oJO8SyRlraF6VOh1Oiqov3CZmG_8lzE2oNZ8f-XrOWk,528
|
|
8
|
+
hexel/compute/_agent.py,sha256=_67qe_I4pxhXiZ7dqRNNXrKILM-nUGGhVXzv2eNWGVQ,1529
|
|
9
|
+
hexel/compute/_instance.py,sha256=PgI2oPeQ6LGLQ_DQ4nOIScRLM87G3VR8Li1I9XS17Fw,1936
|
|
10
|
+
hexel/compute/_sandbox.py,sha256=4I1BJ-gfX80Y-tiMlR_Q4i5ENymCfP5UIRh7OF1cNjQ,1940
|
|
11
|
+
hexel/compute/types.py,sha256=g8eAOzDTbfHdADv8gCIc4PxzrblNZaxkxqsNvuprNw0,14172
|
|
12
|
+
hexel-0.1.0.dist-info/METADATA,sha256=gDydkxiz9mWx7sU3ztWItH90ltTvMQqfnVdDhTZ-MZY,3996
|
|
13
|
+
hexel-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
14
|
+
hexel-0.1.0.dist-info/licenses/LICENSE,sha256=ySBBNUwEDBWR30cEUtGKYC6OaT0HCSY1Unk4YnNJfB8,1069
|
|
15
|
+
hexel-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hexel Studio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|