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 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: dict):
31
- return self.http.request("POST", "/agentflow/", json=body)
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
- def upload_request(self, project_id: str, container_name: str, body: Dict[str, Any]):
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: Dict[str, Any]):
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
- def update(self, project_id: str, body: Dict[str, Any]):
30
- return self.http.request("PUT", f"/v1/project/{project_id}", json=body)
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 Any, Dict, Optional
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: Dict[str, Any]):
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: Dict[str, Any] = {"projectID": project_id}
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wexa-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Official Wexa Python SDK
5
5
  Author: Wexa
6
6
  License: Apache-2.0
@@ -1,22 +1,24 @@
1
1
  wexa_sdk/__init__.py,sha256=dNvZ0fU4v0clBOuZWdd02SLxVfrBF2Ig-efQMOweq-I,1627
2
- wexa_sdk/agentflows.py,sha256=LQ-W5YISsXe32IZp8xcSy6rtxpeVSIOMqqI1E2rlHkg,1259
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=SitroFP9M5Ut-y00iulRScRvzu4TGIRRbCiRGYo5f9E,1063
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=OV-hLsCYRL9coKmxO4I2M5YMXwX6cAzKtePpcn8TH1s,1035
10
+ wexa_sdk/projects.py,sha256=zx5ncmhxbwSiFKU0QpVA01eKNxQW2zPYzPV_njGUCrc,2937
11
11
  wexa_sdk/settings.py,sha256=TDnqYeFBvrrj9kznTUTJxG29sJ02bHxBlqE55DgIfTg,290
12
- wexa_sdk/skills.py,sha256=71Az2xJ39dy6noBmeoHKxHGtd1tY6n4Zzkbf0YxFu9A,1357
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.0.dist-info/METADATA,sha256=WZEHzNIIMLoIwp6B7E1ctiOf9V9nekaiDclogl4DsQg,581
20
- wexa_sdk-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- wexa_sdk-0.1.0.dist-info/top_level.txt,sha256=iXL6c0Kro-mkIoNbjT76txRuoilWB-P7AHhmvKtdXkA,9
22
- wexa_sdk-0.1.0.dist-info/RECORD,,
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,,