wexa-sdk 0.1.0__py3-none-any.whl → 0.1.2__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.
- wexa_sdk/agentflows.py +43 -5
- wexa_sdk/core/__init__.py +1 -0
- wexa_sdk/core/http.py +86 -0
- wexa_sdk/files.py +9 -2
- wexa_sdk/projects.py +72 -5
- wexa_sdk/skills.py +25 -3
- {wexa_sdk-0.1.0.dist-info → wexa_sdk-0.1.2.dist-info}/METADATA +1 -1
- {wexa_sdk-0.1.0.dist-info → wexa_sdk-0.1.2.dist-info}/RECORD +10 -8
- {wexa_sdk-0.1.0.dist-info → wexa_sdk-0.1.2.dist-info}/WHEEL +0 -0
- {wexa_sdk-0.1.0.dist-info → wexa_sdk-0.1.2.dist-info}/top_level.txt +0 -0
wexa_sdk/agentflows.py
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
from typing import Optional, TypedDict
|
|
1
2
|
from .core.http import HttpClient
|
|
2
3
|
|
|
4
|
+
|
|
5
|
+
class AgentflowCreateBody(TypedDict, total=False):
|
|
6
|
+
"""Body for creating an AgentFlow (Coworker).
|
|
7
|
+
|
|
8
|
+
Required:
|
|
9
|
+
- name: str
|
|
10
|
+
- description: str
|
|
11
|
+
- role: str
|
|
12
|
+
- projectID: str
|
|
13
|
+
|
|
14
|
+
Optional: backend-supported fields like agents/processflow, anomaly_detection, cron_details, etc.
|
|
15
|
+
"""
|
|
16
|
+
name: str
|
|
17
|
+
description: str
|
|
18
|
+
role: str
|
|
19
|
+
projectID: str
|
|
20
|
+
|
|
3
21
|
class AgentFlows:
|
|
4
22
|
def __init__(self, http: HttpClient):
|
|
5
23
|
self.http = http
|
|
@@ -27,11 +45,31 @@ class AgentFlows:
|
|
|
27
45
|
def get(self, id: str):
|
|
28
46
|
return self.http.request("GET", f"/agentflow/{id}")
|
|
29
47
|
|
|
30
|
-
def create(self, body:
|
|
31
|
-
|
|
48
|
+
def create(self, body: AgentflowCreateBody, projectID: Optional[str] = None):
|
|
49
|
+
"""
|
|
50
|
+
Create a new AgentFlow (Coworker).
|
|
51
|
+
|
|
52
|
+
Query params:
|
|
53
|
+
- projectID (str, required): Project to create the AgentFlow in. If not provided
|
|
54
|
+
as an argument, this will be inferred from body['projectID'] or body['projectId'].
|
|
55
|
+
|
|
56
|
+
Headers:
|
|
57
|
+
- x-api-key (str, required)
|
|
58
|
+
|
|
59
|
+
Body (application/json):
|
|
60
|
+
- name (str, required)
|
|
61
|
+
- description (str, required)
|
|
62
|
+
- role (str, required)
|
|
63
|
+
- projectID (str, required)
|
|
64
|
+
- ...additional optional fields supported by backend (e.g., agents, anomaly_detection, cron_details)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
- dict: The created AgentFlow JSON (e.g., keys: _id, name, role, projectID, ...)
|
|
68
|
+
"""
|
|
69
|
+
# include projectID in query if provided, or infer from body
|
|
70
|
+
pid = projectID or body.get("projectID") or body.get("projectId")
|
|
71
|
+
params = {"projectID": pid} if pid else None
|
|
72
|
+
return self.http.request("POST", "/agentflow/", params=params, json=body)
|
|
32
73
|
|
|
33
74
|
def update(self, id: str, body: dict):
|
|
34
75
|
return self.http.request("PUT", f"/agentflow/{id}", json=body)
|
|
35
|
-
|
|
36
|
-
def delete(self, project_id: str, id: str):
|
|
37
|
-
return self.http.request("DELETE", f"/agentflow/{project_id}/{id}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .http import HttpClient, ApiError
|
wexa_sdk/core/http.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
RETRY_STATUS = {429, 500, 502, 503, 504}
|
|
9
|
+
|
|
10
|
+
class ApiError(Exception):
|
|
11
|
+
def __init__(self, status: int, detail: Optional[str] = None, code: Optional[str] = None, request_id: Optional[str] = None, raw: Any = None):
|
|
12
|
+
super().__init__(detail or f"API Error {status}")
|
|
13
|
+
self.status = status
|
|
14
|
+
self.code = code
|
|
15
|
+
self.detail = detail
|
|
16
|
+
self.request_id = request_id
|
|
17
|
+
self.raw = raw
|
|
18
|
+
|
|
19
|
+
class HttpClient:
|
|
20
|
+
def __init__(self, base_url: str, api_key: str, user_agent: Optional[str] = None, timeout: Optional[dict] = None, retries: Optional[dict] = None):
|
|
21
|
+
self.base_url = base_url.rstrip("/")
|
|
22
|
+
self.api_key = api_key
|
|
23
|
+
self.user_agent = user_agent
|
|
24
|
+
self.timeout = {
|
|
25
|
+
"connectTimeoutMs": (timeout or {}).get("connectTimeoutMs", 5000),
|
|
26
|
+
"readTimeoutMs": (timeout or {}).get("readTimeoutMs", 30000),
|
|
27
|
+
}
|
|
28
|
+
self.retries = {
|
|
29
|
+
"attempts": (retries or {}).get("attempts", 3),
|
|
30
|
+
"baseDelayMs": (retries or {}).get("baseDelayMs", 500),
|
|
31
|
+
"maxDelayMs": (retries or {}).get("maxDelayMs", 30000),
|
|
32
|
+
}
|
|
33
|
+
self._client = httpx.Client(timeout=self.timeout["readTimeoutMs"] / 1000)
|
|
34
|
+
|
|
35
|
+
def request(self, method: str, path: str, *, params: Optional[Dict[str, Any]] = None, json: Any = None, headers: Optional[Dict[str, str]] = None):
|
|
36
|
+
url = f"{self.base_url}{path}"
|
|
37
|
+
hdrs = {
|
|
38
|
+
"content-type": "application/json",
|
|
39
|
+
"x-api-key": self.api_key,
|
|
40
|
+
"X-Wexa-SDK-Version": "py/0.1.0",
|
|
41
|
+
}
|
|
42
|
+
if self.user_agent:
|
|
43
|
+
hdrs["User-Agent"] = self.user_agent
|
|
44
|
+
if headers:
|
|
45
|
+
hdrs.update(headers)
|
|
46
|
+
|
|
47
|
+
req_id = headers.get("x-client-request-id") if headers else None
|
|
48
|
+
if not req_id:
|
|
49
|
+
req_id = str(int(time.time() * 1000))
|
|
50
|
+
hdrs["x-client-request-id"] = req_id
|
|
51
|
+
|
|
52
|
+
attempt = 0
|
|
53
|
+
last_err: Optional[Exception] = None
|
|
54
|
+
while attempt < self.retries["attempts"]:
|
|
55
|
+
try:
|
|
56
|
+
resp = self._client.request(method, url, params=params, json=json, headers=hdrs)
|
|
57
|
+
res_req_id = resp.headers.get("x-request-id") or req_id
|
|
58
|
+
text = resp.text
|
|
59
|
+
try:
|
|
60
|
+
data = resp.json() if text else None
|
|
61
|
+
except Exception:
|
|
62
|
+
data = text
|
|
63
|
+
if not resp.is_success:
|
|
64
|
+
detail = (isinstance(data, dict) and (data.get("detail") or data.get("error") or data.get("message"))) or text or f"HTTP {resp.status_code}"
|
|
65
|
+
if resp.status_code in RETRY_STATUS and attempt < self.retries["attempts"] - 1:
|
|
66
|
+
time.sleep(self._backoff(attempt) / 1000)
|
|
67
|
+
attempt += 1
|
|
68
|
+
continue
|
|
69
|
+
raise ApiError(status=resp.status_code, detail=detail, request_id=res_req_id, raw=data)
|
|
70
|
+
return data
|
|
71
|
+
except ApiError:
|
|
72
|
+
raise
|
|
73
|
+
except Exception as e: # network error
|
|
74
|
+
last_err = e
|
|
75
|
+
if attempt < self.retries["attempts"] - 1:
|
|
76
|
+
time.sleep(self._backoff(attempt) / 1000)
|
|
77
|
+
attempt += 1
|
|
78
|
+
continue
|
|
79
|
+
raise ApiError(status=0, detail=str(e), request_id=req_id, raw=e)
|
|
80
|
+
raise last_err # type: ignore
|
|
81
|
+
|
|
82
|
+
def _backoff(self, attempt: int) -> int:
|
|
83
|
+
import random
|
|
84
|
+
jitter = random.random() * 0.2 + 0.9
|
|
85
|
+
delay = min(self.retries["maxDelayMs"], int(self.retries["baseDelayMs"] * (2 ** attempt)))
|
|
86
|
+
return int(delay * jitter)
|
wexa_sdk/files.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Any, Dict, Optional
|
|
2
|
+
from typing import Any, Dict, Optional, TypedDict
|
|
3
3
|
|
|
4
4
|
from .core.http import HttpClient
|
|
5
5
|
|
|
@@ -9,7 +9,14 @@ class Files:
|
|
|
9
9
|
|
|
10
10
|
# POST /files/upload?projectID=...&container_name=...
|
|
11
11
|
# body example: { "filenames": ["file.pdf"], "tags": ["resume"], "source_type": "STORAGE", "org_id": "..." }
|
|
12
|
-
|
|
12
|
+
class UploadFilesBody(TypedDict):
|
|
13
|
+
filenames: list[str]
|
|
14
|
+
tags: list[str]
|
|
15
|
+
projectID: str
|
|
16
|
+
source_type: str
|
|
17
|
+
org_id: str
|
|
18
|
+
|
|
19
|
+
def upload_request(self, project_id: str, container_name: str, body: UploadFilesBody):
|
|
13
20
|
params: Dict[str, Any] = {"projectID": project_id, "container_name": container_name}
|
|
14
21
|
return self.http.request("POST", "/files/upload", params=params, json=body)
|
|
15
22
|
|
wexa_sdk/projects.py
CHANGED
|
@@ -1,33 +1,100 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Optional, Dict, Any
|
|
2
|
+
from typing import Optional, Dict, Any, TypedDict
|
|
3
3
|
|
|
4
4
|
from .core.http import HttpClient
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
class ProjectCreateBody(TypedDict, total=False):
|
|
8
|
+
"""Body for creating a project.
|
|
9
|
+
|
|
10
|
+
Required fields:
|
|
11
|
+
- orgId: str
|
|
12
|
+
- projectName: str
|
|
13
|
+
|
|
14
|
+
Optional fields:
|
|
15
|
+
- description: str
|
|
16
|
+
- coworker_role: str
|
|
17
|
+
- status: str (e.g., "published")
|
|
18
|
+
"""
|
|
19
|
+
orgId: str
|
|
20
|
+
projectName: str
|
|
21
|
+
description: str
|
|
22
|
+
coworker_role: str
|
|
23
|
+
status: str
|
|
24
|
+
|
|
6
25
|
class Projects:
|
|
7
26
|
def __init__(self, http: HttpClient):
|
|
8
27
|
self.http = http
|
|
9
28
|
|
|
10
29
|
# Per developers.wexa.ai: POST https://api.wexa.ai/v1/project
|
|
11
|
-
def create(self, body:
|
|
30
|
+
def create(self, body: ProjectCreateBody):
|
|
12
31
|
"""
|
|
13
32
|
Expected body (example):
|
|
14
33
|
{
|
|
15
34
|
"orgId": "67fdea40aac77be632954f0f",
|
|
16
35
|
"projectName": "New",
|
|
17
36
|
"description": "yoooo",
|
|
18
|
-
"coworker_role": "testrole"
|
|
37
|
+
"coworker_role": "testrole",
|
|
38
|
+
"status": "published"
|
|
19
39
|
}
|
|
20
40
|
"""
|
|
21
41
|
return self.http.request("POST", "/v1/project", json=body)
|
|
22
42
|
|
|
43
|
+
def create_simple(
|
|
44
|
+
self,
|
|
45
|
+
*,
|
|
46
|
+
orgId: str,
|
|
47
|
+
projectName: str,
|
|
48
|
+
description: Optional[str] = None,
|
|
49
|
+
coworker_role: Optional[str] = None,
|
|
50
|
+
status: Optional[str] = None,
|
|
51
|
+
):
|
|
52
|
+
"""Convenience wrapper with explicit kwargs for IDE hints.
|
|
53
|
+
|
|
54
|
+
Builds the request body and calls create().
|
|
55
|
+
"""
|
|
56
|
+
body: Dict[str, Any] = {"orgId": orgId, "projectName": projectName}
|
|
57
|
+
if description is not None:
|
|
58
|
+
body["description"] = description
|
|
59
|
+
if coworker_role is not None:
|
|
60
|
+
body["coworker_role"] = coworker_role
|
|
61
|
+
if status is not None:
|
|
62
|
+
body["status"] = status
|
|
63
|
+
return self.create(body) # type: ignore[arg-type]
|
|
64
|
+
|
|
23
65
|
def list(self):
|
|
24
66
|
return self.http.request("GET", "/v1/project")
|
|
25
67
|
|
|
68
|
+
def list_all(self, user_id: str):
|
|
69
|
+
"""
|
|
70
|
+
Get all projects for a given user (organization-wide).
|
|
71
|
+
GET /v1/project/all?userId=...
|
|
72
|
+
|
|
73
|
+
Headers:
|
|
74
|
+
- x-api-key: string (required)
|
|
75
|
+
|
|
76
|
+
Query params:
|
|
77
|
+
- userId: string (required)
|
|
78
|
+
"""
|
|
79
|
+
params = {"userId": user_id}
|
|
80
|
+
return self.http.request("GET", "/v1/project/all", params=params)
|
|
81
|
+
|
|
26
82
|
def get(self, project_id: str):
|
|
27
83
|
return self.http.request("GET", f"/v1/project/{project_id}")
|
|
28
84
|
|
|
29
|
-
|
|
30
|
-
|
|
85
|
+
class ProjectUpdateBody(TypedDict):
|
|
86
|
+
orgId: str
|
|
87
|
+
projectName: str
|
|
88
|
+
description: str
|
|
89
|
+
coworker_role: str
|
|
90
|
+
|
|
91
|
+
def update(self, project_id: str, body: ProjectUpdateBody):
|
|
92
|
+
"""Update a project via PUT /v1/project?projectId=... with required fields.
|
|
93
|
+
|
|
94
|
+
Required body keys: orgId, projectName, description, coworker_role
|
|
95
|
+
"""
|
|
96
|
+
params = {"projectId": project_id}
|
|
97
|
+
return self.http.request("PUT", "/v1/project", params=params, json=body)
|
|
31
98
|
|
|
32
99
|
def delete(self, project_id: str):
|
|
33
100
|
return self.http.request("DELETE", f"/v1/project/{project_id}")
|
wexa_sdk/skills.py
CHANGED
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional, TypedDict, Dict, Any
|
|
3
3
|
|
|
4
4
|
from .core.http import HttpClient
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
class SkillCreateBody(TypedDict):
|
|
8
|
+
"""Body for creating a Skill.
|
|
9
|
+
|
|
10
|
+
Required (user_id can be null but key is required):
|
|
11
|
+
- name: str
|
|
12
|
+
- logo: str
|
|
13
|
+
- connector_name: str
|
|
14
|
+
- description: str
|
|
15
|
+
- projectID: str
|
|
16
|
+
- connector_id: str
|
|
17
|
+
- user_id: Optional[str]
|
|
18
|
+
"""
|
|
19
|
+
name: str
|
|
20
|
+
logo: str
|
|
21
|
+
connector_name: str
|
|
22
|
+
description: str
|
|
23
|
+
projectID: str
|
|
24
|
+
connector_id: str
|
|
25
|
+
user_id: Optional[str]
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
class Skills:
|
|
7
29
|
def __init__(self, http: HttpClient):
|
|
8
30
|
self.http = http
|
|
9
31
|
|
|
10
32
|
# POST /skills/
|
|
11
|
-
def create(self, body:
|
|
33
|
+
def create(self, body: SkillCreateBody):
|
|
12
34
|
return self.http.request("POST", "/skills/", json=body)
|
|
13
35
|
|
|
14
36
|
# GET /skills/?projectID=...&limit=...
|
|
15
37
|
def list(self, project_id: str, *, limit: Optional[int] = None):
|
|
16
|
-
params
|
|
38
|
+
params = {"projectID": project_id}
|
|
17
39
|
if limit is not None:
|
|
18
40
|
params["limit"] = limit
|
|
19
41
|
return self.http.request("GET", "/skills/", params=params)
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
wexa_sdk/__init__.py,sha256=dNvZ0fU4v0clBOuZWdd02SLxVfrBF2Ig-efQMOweq-I,1627
|
|
2
|
-
wexa_sdk/agentflows.py,sha256=
|
|
2
|
+
wexa_sdk/agentflows.py,sha256=_1DJYXVi6Jyj9-jn0UtO1C7X_sdTcfdp0qdMtQZPQKo,2543
|
|
3
3
|
wexa_sdk/analytics.py,sha256=PVWvRvuyxOAbwxxAoILquESyb2Jw3cJGogbBu2ATRH4,345
|
|
4
4
|
wexa_sdk/connectors_mgmt.py,sha256=7vCT4ilez2YjZnfFEzb0raglTwweqxomqtJOb36RVT4,2229
|
|
5
5
|
wexa_sdk/executions.py,sha256=-bVXo7ESA8AYZtc-tPDi28qWKJg_eYEQCo1kSYgE1P8,2509
|
|
6
|
-
wexa_sdk/files.py,sha256=
|
|
6
|
+
wexa_sdk/files.py,sha256=K2m6gxs_8XDhlW4W08DhSc-HwrMx3r2S5dOFvltLTqc,1235
|
|
7
7
|
wexa_sdk/inbox.py,sha256=aKFmST2FX09mDYXyrqNpUYTsq6z0iJG4qtsCeMWKPdo,1957
|
|
8
8
|
wexa_sdk/marketplace.py,sha256=jhpeSWLU_BLFJcbwx_vcLeUff3UEPkylyhVIm_Fcb5Y,1609
|
|
9
9
|
wexa_sdk/project_members.py,sha256=TssDroghwYMsPRibZ1dNvKynzMtgIUN1TDFsmc2AuW4,514
|
|
10
|
-
wexa_sdk/projects.py,sha256=
|
|
10
|
+
wexa_sdk/projects.py,sha256=zx5ncmhxbwSiFKU0QpVA01eKNxQW2zPYzPV_njGUCrc,2937
|
|
11
11
|
wexa_sdk/settings.py,sha256=TDnqYeFBvrrj9kznTUTJxG29sJ02bHxBlqE55DgIfTg,290
|
|
12
|
-
wexa_sdk/skills.py,sha256=
|
|
12
|
+
wexa_sdk/skills.py,sha256=ZOFdspJsns_X88nBMrhaFWWcTKuh5afk0hK73-Qzs9M,1799
|
|
13
13
|
wexa_sdk/tables.py,sha256=JYGK1zWRWZgZGbiRyv-ko3XKdpxZv0piwb02h0v5ODw,2753
|
|
14
14
|
wexa_sdk/tasks.py,sha256=YRXMxQwgpiflAxyUqedyo4LB6bsO7zsiM5uP0uE8_Ds,1349
|
|
15
15
|
wexa_sdk/connectors/__init__.py,sha256=eEtSQ8oXlYnoD3CUxPCF3Il6bgGyN6zfKI3_Pu41jXo,51
|
|
16
16
|
wexa_sdk/connectors/core.py,sha256=4dFp0CVCysUsIOT7l9V7bzXu1jRx9EBGZv2_bQkKPsQ,1188
|
|
17
17
|
wexa_sdk/connectors/google_drive.py,sha256=Dibfoy8uKcGygmjCnrolPhKq5Fy5ye5-ee_kI4XD7kY,945
|
|
18
|
+
wexa_sdk/core/__init__.py,sha256=K-bUeDdR3iblN9BNwMQ-ZMbOcAJSAUkNwuoVz5L3fC4,39
|
|
19
|
+
wexa_sdk/core/http.py,sha256=4O59C5VlVhxaQNkjJ0UpARZk3g-rEoOxEbwE0u9NWbY,3721
|
|
18
20
|
wexa_sdk/models/__init__.py,sha256=yxQUjah8RTJh531y8QQXEeh5dp0d5k55dsrVvR4O1N8,75
|
|
19
|
-
wexa_sdk-0.1.
|
|
20
|
-
wexa_sdk-0.1.
|
|
21
|
-
wexa_sdk-0.1.
|
|
22
|
-
wexa_sdk-0.1.
|
|
21
|
+
wexa_sdk-0.1.2.dist-info/METADATA,sha256=hIdfzWXxN8YiHvOXe2688JcF0V6fl4IhOr-D84UWXNE,581
|
|
22
|
+
wexa_sdk-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
wexa_sdk-0.1.2.dist-info/top_level.txt,sha256=iXL6c0Kro-mkIoNbjT76txRuoilWB-P7AHhmvKtdXkA,9
|
|
24
|
+
wexa_sdk-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|