wexa-sdk 0.1.2__py3-none-any.whl → 0.1.11__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/__init__.py CHANGED
@@ -13,6 +13,8 @@ from .analytics import Analytics
13
13
  from .project_members import ProjectMembers
14
14
  from .connectors_mgmt import ConnectorsMgmt
15
15
  from .files import Files
16
+ from .llm import Llm
17
+ from .schedules import Schedules
16
18
 
17
19
  class WexaClient:
18
20
  def __init__(self, base_url: str, api_key: str, user_agent: str | None = None, timeout: dict | None = None, retries: dict | None = None, polling: dict | None = None):
@@ -31,6 +33,8 @@ class WexaClient:
31
33
  self.project_members = ProjectMembers(self.http)
32
34
  self.connectors_mgmt = ConnectorsMgmt(self.http)
33
35
  self.files = Files(self.http)
36
+ self.llm = Llm(self.http)
37
+ self.schedules = Schedules(self.http)
34
38
 
35
39
  def _action(self, *args, **kwargs):
36
40
  # Kept for backward compatibility; delegate to connectors.action
wexa_sdk/agentflows.py CHANGED
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  from typing import Optional, TypedDict
2
3
  from .core.http import HttpClient
3
4
 
@@ -73,3 +74,67 @@ class AgentFlows:
73
74
 
74
75
  def update(self, id: str, body: dict):
75
76
  return self.http.request("PUT", f"/agentflow/{id}", json=body)
77
+
78
+ def add_skilled_agent(self, agentflow_id: str, *, projectID: str, body: dict):
79
+ """
80
+ Add a skilled agent to an AgentFlow.
81
+
82
+ POST /agentflow/{agentflow_id}/skilled?projectID=...
83
+ Body: skilled agent configuration (role, title, skills, context, llm, memory, prompt, triggers, etc.)
84
+ """
85
+ params = {"projectID": projectID}
86
+ return self.http.request("POST", f"/agentflow/{agentflow_id}/skilled", params=params, json=body)
87
+
88
+ def get_by_user_and_project(self, agentflow_id: str, executed_by: str, projectID: str):
89
+ """
90
+ Get AgentFlow by user and project.
91
+
92
+ GET /agentflow/{agentflow_id}/user/{executed_by}/project/{projectID}
93
+ """
94
+ return self.http.request("GET", f"/agentflow/{agentflow_id}/user/{executed_by}/project/{projectID}")
95
+
96
+ # Typed body for updating a skilled agent to enable IDE suggestions
97
+ class UpdateSkilledAgentBody(TypedDict, total=False):
98
+ """Body for updating a skilled agent within an AgentFlow.
99
+
100
+ Required (per API docs):
101
+ - role: str
102
+ - title: str
103
+ - skills: list[str]
104
+ - prompt: { template: str, variables: list, display_template: str }
105
+ - context: list
106
+ - triggers: list
107
+ - llm: { model: str, max_tokens: int, temperature: int }
108
+ - role_description: str
109
+ - memory: { memory_type: str }
110
+ - has_knowledge_base: bool
111
+ - is_user_specific_task: bool
112
+ - is_preview_mode_enabled: bool
113
+ """
114
+ role: str
115
+ title: str
116
+ skills: list[str]
117
+ prompt: dict
118
+ context: list
119
+ triggers: list
120
+ llm: dict
121
+ role_description: str
122
+ memory: dict
123
+ has_knowledge_base: bool
124
+ is_user_specific_task: bool
125
+ is_preview_mode_enabled: bool
126
+
127
+ def update_skilled_agent(self, agentflow_id: str, agent_id: str, *, projectID: str, body: UpdateSkilledAgentBody | dict):
128
+ """Update a skilled agent within an AgentFlow.
129
+
130
+ Endpoint: POST /agentflow/{agentflow_id}/update/skilled/{agent_id}?projectID=...
131
+
132
+ Args:
133
+ agentflow_id: ID of the AgentFlow
134
+ agent_id: ID of the skilled agent inside the AgentFlow
135
+ projectID: Project ID (query param, required)
136
+ body: Update payload, see UpdateSkilledAgentBody for suggested keys
137
+ """
138
+ params = {"projectID": projectID} if projectID else None
139
+ path = f"/agentflow/{agentflow_id}/update/skilled/{agent_id}"
140
+ return self.http.request("POST", path, params=params, json=body)
@@ -1,9 +1,35 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Optional
2
+ from typing import Any, Optional, TypedDict
3
3
 
4
4
  from ..core.http import HttpClient
5
5
  from .google_drive import GoogleDrive
6
6
 
7
+ class ConnectorConfigBody(TypedDict, total=False):
8
+ """JSON body for configuring a connector via POST /actions/{CATEGORY}/config.
9
+
10
+ Required by backend (typical fields):
11
+ - name: str
12
+ - description: str
13
+ - category: str
14
+ - org_id: str
15
+ - projectID: str # NOTE: camelCase as required by backend
16
+ - logo: str
17
+ - ui_form: list
18
+
19
+ Optional/variable:
20
+ - config: dict
21
+ - enabled: bool
22
+ """
23
+ name: str
24
+ description: str
25
+ category: str
26
+ org_id: str
27
+ projectID: str
28
+ logo: str
29
+ ui_form: list
30
+ config: dict
31
+ enabled: bool
32
+
7
33
  class Connectors:
8
34
  def __init__(self, http: HttpClient):
9
35
  self.http = http
@@ -16,8 +42,10 @@ class Connectors:
16
42
  return self.http.request("POST", path, params=params, json=body)
17
43
 
18
44
  # POST /actions/{CATEGORY}/config?projectID=...
19
- def set_config(self, category: str, project_id: str, body: dict) -> Any:
20
- return self.http.request("POST", f"/actions/{category}/config", params={"projectID": project_id}, json=body)
45
+ def set_config(self, category: str, project_id: str, body: ConnectorConfigBody | dict) -> Any:
46
+ # Ensure body contains the required camelCase field expected by the backend
47
+ json_body = {**(body or {}), "projectID": project_id}
48
+ return self.http.request("POST", f"/actions/{category}/config", params={"projectID": project_id}, json=json_body)
21
49
 
22
50
  # GET /actions/{CATEGORY}/config/{projectID}
23
51
  def get_config(self, category: str, project_id: str) -> Any:
wexa_sdk/core/http.py CHANGED
@@ -37,7 +37,7 @@ class HttpClient:
37
37
  hdrs = {
38
38
  "content-type": "application/json",
39
39
  "x-api-key": self.api_key,
40
- "X-Wexa-SDK-Version": "py/0.1.0",
40
+ "X-Wexa-SDK-Version": "py/0.1.11",
41
41
  }
42
42
  if self.user_agent:
43
43
  hdrs["User-Agent"] = self.user_agent
wexa_sdk/executions.py CHANGED
@@ -12,6 +12,17 @@ class Executions:
12
12
  self.polling = polling or {}
13
13
 
14
14
  def start(self, payload: dict, *, projectID: Optional[str] = None):
15
+ """
16
+ Create/Start an execution.
17
+
18
+ POST /execute_flow?projectID=...
19
+ Body requires at least:
20
+ - agentflow_id: str
21
+ - executed_by: str
22
+ - goal: str
23
+ - input_variables: dict
24
+ - projectID: str (backend may also accept via query)
25
+ """
15
26
  params = {"projectID": projectID} if projectID else None
16
27
  return self.http.request("POST", "/execute_flow", json=payload, params=params)
17
28
 
@@ -30,28 +41,17 @@ class Executions:
30
41
  def cancel(self, execution_id: str):
31
42
  return self.http.request("POST", f"/execute_flow/{execution_id}/cancel")
32
43
 
33
- def approve_preview(self, execution_id: str):
34
- return self.http.request("PUT", f"/execute_flow/{execution_id}/approve_preview")
35
-
36
- def approve_anomaly(self, execution_id: str):
37
- return self.http.request("PUT", f"/execute_flow/{execution_id}/approve_anomaly")
38
-
39
- def update_runtime_input(self, execution_id: str, body: dict):
40
- return self.http.request("PUT", f"/execute_flow/{execution_id}/runtime_input", json=body)
41
-
42
- def wait(self, execution_id: str, *, interval_ms: Optional[int] = None, timeout_ms: Optional[int] = None, is_terminal: Optional[Callable[[str], bool]] = None):
43
- interval = interval_ms or self.polling.get("intervalMs", 2000)
44
- timeout = timeout_ms or self.polling.get("timeoutMs", 30 * 60 * 1000)
45
- jitter = self.polling.get("jitter", 0.2)
46
- term = is_terminal or (lambda s: (s or "").lower() in DEFAULT_TERMINAL)
47
-
48
- start = time.time()
49
- while True:
50
- info = self.get(execution_id)
51
- status = (info.get("status") or info.get("state") or "").lower()
52
- if term(status):
53
- return info
54
- if (time.time() - start) * 1000 > timeout:
55
- raise TimeoutError("Execution wait timeout")
56
- j = 1 + ((2 * (time.time() % 1) - 1) * jitter)
57
- time.sleep(max(0.2, (interval * j) / 1000))
44
+ def execute(self, execution_id: str, *, projectID: str, body: Optional[dict] = None):
45
+ """
46
+ Execute an existing execution by ID.
47
+
48
+ POST /execute_flow/{execution_id}/execute?projectID=...
49
+ The backend may expect execution_id and/or projectID in the body as well.
50
+ """
51
+ params = {"projectID": projectID}
52
+ json_body = {"execution_id": execution_id, "projectID": projectID}
53
+ if body:
54
+ json_body.update(body)
55
+ return self.http.request("POST", f"/execute_flow/{execution_id}/execute", params=params, json=json_body)
56
+
57
+ # Removed wait/approve/update-runtime endpoints per request
wexa_sdk/files.py CHANGED
@@ -25,6 +25,6 @@ class Files:
25
25
  params = {"projectID": project_id} if project_id else None
26
26
  return self.http.request("GET", f"/file/{file_id}/", params=params)
27
27
 
28
- # GET /files/{connectorId}/connector/
29
- def list_by_connector(self, connector_id: str):
30
- return self.http.request("GET", f"/files/{connector_id}/connector/")
28
+ # GET /files/{projectID}/connector/{connector_id}
29
+ def list_by_connector(self, project_id: str, connector_id: str):
30
+ return self.http.request("GET", f"/files/{project_id}/connector/{connector_id}")
wexa_sdk/inbox.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, Literal
3
3
 
4
4
  from .core.http import HttpClient
5
5
 
@@ -7,31 +7,95 @@ class Inbox:
7
7
  def __init__(self, http: HttpClient):
8
8
  self.http = http
9
9
 
10
- # POST /inbox/create
11
- def create(self, body: Dict[str, Any]):
10
+ class InboxCreateBody(TypedDict, total=False):
11
+ _id: str
12
+ type: str
13
+ status: str
14
+ created_at: float
15
+ updated_at: float
16
+ agent_id: str
17
+ coworker_id: str
18
+ coworker_name: str
19
+ agent_title: str
20
+ summary: str
21
+ execution_id: str
22
+ projectID: str
23
+ # Allow extra fields if backend accepts more
24
+ Name: str
25
+ Description: str
26
+
27
+ def create(self, body: InboxCreateBody):
28
+ """
29
+ Create inbox
30
+ POST /inbox/create
31
+ Creates a new inbox request entry (preview, runtime_input, or anomaly_detection).
32
+ """
12
33
  return self.http.request("POST", "/inbox/create", json=body)
13
34
 
14
- # GET /inbox?projectID=...&limit=...
15
- def list(self, project_id: str, *, limit: Optional[int] = None):
35
+ def list(
36
+ self,
37
+ project_id: str,
38
+ *,
39
+ limit: Optional[int] = 100,
40
+ status: Optional[str] = None,
41
+ type: Optional[str] = None,
42
+ search_key: Optional[str] = None,
43
+ after_id: Optional[str] = None,
44
+ view: Literal["ui", "studio"] = "ui",
45
+ ):
46
+ """
47
+ GET /inbox
48
+ Query: projectID (required), limit, status, type, search_key, after_id, view
49
+ """
16
50
  params: Dict[str, Any] = {"projectID": project_id}
17
51
  if limit is not None:
18
52
  params["limit"] = limit
53
+ if status is not None:
54
+ params["status"] = status
55
+ if type is not None:
56
+ params["type"] = type
57
+ if search_key is not None:
58
+ params["search_key"] = search_key
59
+ if after_id is not None:
60
+ params["after_id"] = after_id
61
+ if view:
62
+ params["view"] = view
19
63
  return self.http.request("GET", "/inbox", params=params)
20
64
 
21
- # POST /inbox/update/runtime_input/?projectID=...
22
- def update_runtime(self, project_id: str, body: Dict[str, Any]):
23
- return self.http.request("POST", "/inbox/update/runtime_input/", params={"projectID": project_id}, json=body)
65
+ class UpdateRuntimeBody(TypedDict):
66
+ is_submitted: bool
67
+ values: Dict[str, str]
68
+ agent_id: str
69
+
70
+ def update_runtime(self, execution_id: str, project_id: Optional[str], body: UpdateRuntimeBody):
71
+ """
72
+ Update inbox at runtime
73
+ POST /inbox/update/runtime_input/{execution_id}?projectID=...
74
+ """
75
+ params = {"projectID": project_id} if project_id else None
76
+ return self.http.request("POST", f"/inbox/update/runtime_input/{execution_id}", params=params, json=body)
24
77
 
25
- # POST /inbox/update/anomaly_detection/?projectID=...
26
- def update_anomaly(self, project_id: str, body: Dict[str, Any]):
27
- return self.http.request("POST", "/inbox/update/anomaly_detection/", params={"projectID": project_id}, json=body)
78
+ class UpdateAnomalyBody(TypedDict):
79
+ is_approved: bool
80
+
81
+ def update_anomaly(self, execution_id: str, project_id: Optional[str], body: UpdateAnomalyBody):
82
+ """
83
+ Update anomaly detection inbox
84
+ POST /inbox/update/anomaly_detection/{execution_id}?projectID=...
85
+ """
86
+ params = {"projectID": project_id} if project_id else None
87
+ return self.http.request("POST", f"/inbox/update/anomaly_detection/{execution_id}", params=params, json=body)
28
88
 
29
- # POST /inbox/update/preview/?projectID=...
30
- def update_preview(self, project_id: str, body: Dict[str, Any]):
31
- return self.http.request("POST", "/inbox/update/preview/", params={"projectID": project_id}, json=body)
89
+ class UpdatePreviewBody(TypedDict):
90
+ agent_id: str
91
+ is_approved: bool
92
+ preview_input: Dict[str, Any]
32
93
 
33
- # POST /inbox/update/preview/{execution_id}?projectID=...
34
- def update_preview_by_execution(self, execution_id: str, body: Dict[str, Any], project_id: Optional[str] = None):
94
+ def update_preview(self, execution_id: str, project_id: Optional[str], body: UpdatePreviewBody):
95
+ """
96
+ Update Preview Inbox (Approve or Draft)
97
+ POST /inbox/update/preview/{execution_id}?projectID=...
98
+ """
35
99
  params = {"projectID": project_id} if project_id else None
36
100
  return self.http.request("POST", f"/inbox/update/preview/{execution_id}", params=params, json=body)
37
101
 
wexa_sdk/llm.py ADDED
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+ from typing import TypedDict, List, Literal, Optional, Any
3
+
4
+ from .core.http import HttpClient
5
+
6
+
7
+ AllowedModel = Literal[
8
+ "bedrock/amazon.nova-pro-v1",
9
+ "openai/gpt-4o",
10
+ "anthropic/claude-3-5-sonnet",
11
+ "cohere/command-r-plus",
12
+ ]
13
+
14
+
15
+ class ChatMessage(TypedDict):
16
+ role: Literal["system", "user", "assistant"]
17
+ content: str
18
+
19
+
20
+ class LlmRequest(TypedDict, total=False):
21
+ model: AllowedModel | str
22
+ messages: List[ChatMessage]
23
+ temperature: float
24
+ maxTokens: int
25
+ stream: bool
26
+
27
+
28
+ class Llm:
29
+ def __init__(self, http: HttpClient):
30
+ self.http = http
31
+
32
+ def llm_call(self, body: LlmRequest) -> Any:
33
+ """
34
+ Execute an LLM call.
35
+
36
+ POST /llm/execute/calls
37
+
38
+ Example:
39
+ {
40
+ "model": "bedrock/amazon.nova-pro-v1",
41
+ "messages": [{"role": "user", "content": "Hello, how are you?"}]
42
+ }
43
+ """
44
+ return self.http.request("POST", "/llm/execute/calls", json=body)
45
+
46
+
wexa_sdk/projects.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from typing import Optional, Dict, Any, TypedDict
3
+ from urllib.parse import quote
3
4
 
4
5
  from .core.http import HttpClient
5
6
 
@@ -29,7 +30,9 @@ class Projects:
29
30
  # Per developers.wexa.ai: POST https://api.wexa.ai/v1/project
30
31
  def create(self, body: ProjectCreateBody):
31
32
  """
32
- Expected body (example):
33
+ Create a project with a request body.
34
+
35
+ Example body:
33
36
  {
34
37
  "orgId": "67fdea40aac77be632954f0f",
35
38
  "projectName": "New",
@@ -49,10 +52,7 @@ class Projects:
49
52
  coworker_role: Optional[str] = None,
50
53
  status: Optional[str] = None,
51
54
  ):
52
- """Convenience wrapper with explicit kwargs for IDE hints.
53
-
54
- Builds the request body and calls create().
55
- """
55
+ """Convenience wrapper: builds the body and calls create(body)."""
56
56
  body: Dict[str, Any] = {"orgId": orgId, "projectName": projectName}
57
57
  if description is not None:
58
58
  body["description"] = description
@@ -62,8 +62,6 @@ class Projects:
62
62
  body["status"] = status
63
63
  return self.create(body) # type: ignore[arg-type]
64
64
 
65
- def list(self):
66
- return self.http.request("GET", "/v1/project")
67
65
 
68
66
  def list_all(self, user_id: str):
69
67
  """
@@ -79,6 +77,40 @@ class Projects:
79
77
  params = {"userId": user_id}
80
78
  return self.http.request("GET", "/v1/project/all", params=params)
81
79
 
80
+ def get_all(
81
+ self,
82
+ *,
83
+ status: Optional[str] = None,
84
+ user_id: Optional[str] = None,
85
+ org_id: Optional[str] = None,
86
+ page: Optional[int] = None,
87
+ limit: Optional[int] = None,
88
+ ):
89
+ """
90
+ Get all projects with optional filters and pagination.
91
+ GET /v1/project?status=...&userId=...&orgId=...&page=...&limit=...
92
+
93
+ Args:
94
+ status: Optional project status filter (e.g., "published").
95
+ user_id: Optional user filter.
96
+ org_id: Optional organization filter.
97
+ page: Optional page number (int).
98
+ limit: Optional page size (int).
99
+ """
100
+ params: Dict[str, Any] = {}
101
+ if status is not None:
102
+ params["status"] = status
103
+ if user_id is not None:
104
+ params["userId"] = user_id
105
+ if org_id is not None:
106
+ params["orgId"] = org_id
107
+ if page is not None:
108
+ params["page"] = page
109
+ if limit is not None:
110
+ params["limit"] = limit
111
+
112
+ return self.http.request("GET", "/v1/project", params=params)
113
+
82
114
  def get(self, project_id: str):
83
115
  return self.http.request("GET", f"/v1/project/{project_id}")
84
116
 
@@ -98,3 +130,12 @@ class Projects:
98
130
 
99
131
  def delete(self, project_id: str):
100
132
  return self.http.request("DELETE", f"/v1/project/{project_id}")
133
+
134
+ def get_by_project_name(self, project_name: str):
135
+ """
136
+ Get project by projectName.
137
+
138
+ GET /project/projectName/{projectName}
139
+ """
140
+ safe = quote(project_name, safe="")
141
+ return self.http.request("GET", f"/project/projectName/{safe}")
wexa_sdk/schedules.py ADDED
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+ from typing import TypedDict, Optional, Dict, Any
3
+
4
+ from .core.http import HttpClient
5
+
6
+
7
+ class CoworkerScheduleCreateBody(TypedDict):
8
+ coworker_id: str
9
+ goal: Dict[str, Any]
10
+ template: str
11
+ display_template: str
12
+ schedule: int
13
+
14
+
15
+ class CoworkerScheduleUpdateBody(TypedDict, total=False):
16
+ goal: Dict[str, Any]
17
+ template: str
18
+ display_template: str
19
+ schedule: int
20
+
21
+
22
+ class Schedules:
23
+ def __init__(self, http: HttpClient):
24
+ self.http = http
25
+
26
+ def list_coworker_schedules(
27
+ self,
28
+ coworker_id: str | None,
29
+ *,
30
+ projectID: str,
31
+ limit: int = 20,
32
+ page_no: int = 1,
33
+ status: Optional[str] = None,
34
+ type: Optional[str] = None,
35
+ search_key: Optional[str] = None,
36
+ ):
37
+ """
38
+ GET /schedules/coworker
39
+
40
+ Query params: projectID (required), limit, page_no, coworker_id, status, type, search_key
41
+ """
42
+ params: Dict[str, Any] = {"projectID": projectID, "limit": limit, "page_no": page_no}
43
+ if coworker_id:
44
+ params["coworker_id"] = coworker_id
45
+ if status is not None:
46
+ params["status"] = status
47
+ if type is not None:
48
+ params["type"] = type
49
+ if search_key is not None:
50
+ params["search_key"] = search_key
51
+ return self.http.request("GET", "/schedules/coworker", params=params)
52
+
53
+ def create_coworker_schedule(self, *, projectID: str, body: CoworkerScheduleCreateBody):
54
+ """
55
+ POST /schedule/coworker?projectID=...
56
+ Body: coworker_id, goal, template, display_template, schedule
57
+ """
58
+ params = {"projectID": projectID}
59
+ return self.http.request("POST", "/schedule/coworker", params=params, json=body)
60
+
61
+ def get_coworker_schedule(self, id: str):
62
+ """
63
+ GET /schedule/coworker/{id}
64
+ """
65
+ return self.http.request("GET", f"/schedule/coworker/{id}")
66
+
67
+ def update_coworker_schedule(self, id: str, *, projectID: str, body: CoworkerScheduleUpdateBody):
68
+ """
69
+ PATCH /schedule/coworker/{id}?projectID=...
70
+ """
71
+ params = {"projectID": projectID}
72
+ return self.http.request("PATCH", f"/schedule/coworker/{id}", params=params, json=body)
73
+
74
+ def delete_coworker_schedule(self, id: str, *, projectID: str):
75
+ """
76
+ DELETE /schedule/coworker/{id}?projectID=...
77
+ """
78
+ params = {"projectID": projectID}
79
+ return self.http.request("DELETE", f"/schedule/coworker/{id}", params=params)
80
+
81
+
wexa_sdk/tables.py CHANGED
@@ -1,19 +1,79 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Optional
2
+ from typing import Any, Optional, TypedDict, List, Dict, Union
3
3
 
4
4
  from .core.http import HttpClient
5
5
 
6
+ class ObjectField(TypedDict, total=False):
7
+ """Field descriptor for object-type columns."""
8
+ key: str
9
+ keyType: str
10
+
11
+
12
+ class AgentflowTrigger(TypedDict, total=False):
13
+ """Trigger configuration attached to a table or column.
14
+
15
+ Note: exact schemas for `condition` and `filters` may evolve; we leave them open.
16
+ """
17
+ _id: str
18
+ id: str
19
+ condition: Dict[str, Any]
20
+ name: Optional[str]
21
+ goal: str
22
+ agentflow_id: Optional[str]
23
+ filters: List[Dict[str, Any]]
24
+ schedule_time: Optional[str]
25
+ event: str
26
+ start_from_agent_id: Optional[str]
27
+ trigger_type: str # e.g. "coworker"
28
+
29
+
30
+ class Column(TypedDict, total=False):
31
+ """Column definition for a table."""
32
+ column_name: str
33
+ column_type: str
34
+ column_id: str
35
+ array_type: Optional[str]
36
+ default_value: Union[Any, List[Any], Dict[str, Any]]
37
+ object_fields: List[ObjectField]
38
+ triggers: List[AgentflowTrigger]
39
+ enum_options: List[str]
40
+
41
+
42
+ class CreateTableInput(TypedDict, total=False):
43
+ """Typed input for creating a table.
44
+
45
+ Required keys: projectID, table_name
46
+ Optional keys: columns, triggers
47
+ """
48
+ projectID: str
49
+ table_name: str
50
+ columns: List[Column]
51
+ triggers: List[AgentflowTrigger]
52
+
53
+
6
54
  class Tables:
7
55
  def __init__(self, http: HttpClient):
8
56
  self.http = http
9
57
 
10
58
  # Tables
11
- def create_table(self, project_id: str, spec: dict):
59
+ def create_table(self, project_id: str, spec: CreateTableInput):
60
+ """Create a new table.
61
+
62
+ Args:
63
+ project_id: The project ID (placed into query as `projectID`).
64
+ spec: Table specification containing at least `table_name`.
65
+
66
+ The backend expects `projectID` in both query params and JSON body.
67
+ """
12
68
  # API expects projectID as query param and in body with 'projectID' casing
13
69
  params = {"projectID": project_id}
14
70
  body = {"projectID": project_id, **spec}
15
71
  return self.http.request("POST", "/create/table", params=params, json=body)
16
72
 
73
+ # New: POST /storage/{projectID}/{collection_name}
74
+ def create_records_by_collection(self, project_id: str, collection_name: str, records: List[dict]):
75
+ return self.http.request("POST", f"/storage/{project_id}/{collection_name}", json=records)
76
+
17
77
  def list_tables(self, project_id: str):
18
78
  return self.http.request("GET", f"/storage/{project_id}")
19
79
 
@@ -36,6 +96,26 @@ class Tables:
36
96
  def delete_column(self, project_id: str, column_id: str):
37
97
  return self.http.request("DELETE", f"/delete/column/{project_id}", json={"columnId": column_id})
38
98
 
99
+ # New: POST /column/storage/{projectID}/{table_id}?ignore_existing_columns=...
100
+ def add_columns(self, project_id: str, table_id: str, columns: List[Column], ignore_existing_columns: Optional[bool] = None):
101
+ params: Dict[str, Any] = {}
102
+ if ignore_existing_columns is not None:
103
+ params["ignore_existing_columns"] = ignore_existing_columns
104
+ return self.http.request("POST", f"/column/storage/{project_id}/{table_id}", params=params or None, json=columns)
105
+
106
+ # New: PUT /edit/columns/{projectId} with rename body
107
+ def update_column_name(self, project_id: str, *, column_id: str, column_name: str, table_id: str):
108
+ body = {"column_id": column_id, "column_name": column_name, "table_id": table_id}
109
+ return self.http.request("PUT", f"/edit/columns/{project_id}", json=body)
110
+
111
+ # New: PATCH /edit/columns/{table_id} with full Column
112
+ def patch_column(self, table_id: str, column: Column):
113
+ return self.http.request("PATCH", f"/edit/columns/{table_id}", json=column)
114
+
115
+ # New: DELETE /delete/column/{projectId} body { table_id, column_id }
116
+ def delete_column_extended(self, project_id: str, *, table_id: str, column_id: str):
117
+ return self.http.request("DELETE", f"/delete/column/{project_id}", json={"table_id": table_id, "column_id": column_id})
118
+
39
119
  # Records
40
120
  def create_record(self, project_id: str, table_id: str, record: dict):
41
121
  return self.http.request("POST", f"/storage/{project_id}/{table_id}", json=record)
@@ -52,8 +132,29 @@ class Tables:
52
132
  def list_records(self, project_id: str, table_id: str, query: Optional[dict] = None):
53
133
  return self.http.request("GET", f"/storage/{project_id}/{table_id}", params=query)
54
134
 
55
- def bulk_upsert(self, project_id: str, table_id: str, records: list[dict]):
56
- return self.http.request("POST", f"/bulk/storage/{project_id}/{table_id}", json={"records": records})
135
+ # New: DELETE /storage/{projectID}/{tableId} body { storage_ids: [] }
136
+ def delete_records_bulk(self, project_id: str, table_id: str, storage_ids: List[str]):
137
+ return self.http.request("DELETE", f"/storage/{project_id}/{table_id}", json={"storage_ids": storage_ids})
138
+
139
+ # New: PUT /bulk/storage/{projectID}/{table_id} body { records, record_ids: { storage_ids: [] } }
140
+ def bulk_update_records(self, project_id: str, table_id: str, *, records: Dict[str, Any], record_ids: Dict[str, Any]):
141
+ return self.http.request("PUT", f"/bulk/storage/{project_id}/{table_id}", json={"records": records, "record_ids": record_ids})
57
142
 
58
143
  def export(self, project_id: str, table_id: str):
59
144
  return self.http.request("GET", f"/table_data/storage/{table_id}/export")
145
+
146
+ # New: PUT /table/rename/{projectID} body { table_id, table_name, triggers? }
147
+ def rename_table_extended(self, project_id: str, *, table_id: str, table_name: str, triggers: Optional[List[AgentflowTrigger]] = None):
148
+ body: Dict[str, Any] = {"table_id": table_id, "table_name": table_name}
149
+ if triggers is not None:
150
+ body["triggers"] = triggers
151
+ return self.http.request("PUT", f"/table/rename/{project_id}", json=body)
152
+
153
+ # New: POST /table/column_mapper
154
+ def column_mapper(self, *, column_names: List[Dict[str, str]], csv_headers: List[str]):
155
+ body = {"column_names": column_names, "csv_headers": csv_headers}
156
+ return self.http.request("POST", "/table/column_mapper", json=body)
157
+
158
+ # New: POST /table/fieldcount/{project_id}/{table_id}
159
+ def field_count(self, project_id: str, table_id: str, filters: List[Dict[str, Any]]):
160
+ return self.http.request("POST", f"/table/fieldcount/{project_id}/{table_id}", json=filters)
wexa_sdk/tasks.py CHANGED
@@ -9,14 +9,15 @@ class Tasks:
9
9
 
10
10
  # GET /tasks/?projectID=...&limit=...&skip=...&created_by=...
11
11
  def list(self, project_id: str, *, limit: Optional[int] = None, skip: Optional[int] = None, created_by: Optional[str] = None):
12
- params: Dict[str, Any] = {"projectID": project_id}
12
+ api_url = f"/tasks/{project_id}"
13
+ params: Dict[str, Any] = {}
13
14
  if limit is not None:
14
15
  params["limit"] = limit
15
16
  if skip is not None:
16
17
  params["skip"] = skip
17
18
  if created_by:
18
19
  params["created_by"] = created_by
19
- return self.http.request("GET", "/tasks/", params=params)
20
+ return self.http.request("GET", api_url, params=params)
20
21
 
21
22
  # GET /task/{id}?projectID=...
22
23
  def get(self, task_id: str, project_id: Optional[str] = None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wexa-sdk
3
- Version: 0.1.2
3
+ Version: 0.1.11
4
4
  Summary: Official Wexa Python SDK
5
5
  Author: Wexa
6
6
  License: Apache-2.0
@@ -0,0 +1,26 @@
1
+ wexa_sdk/__init__.py,sha256=_KBd7MB0hirHpXmQ8AgaG_Pso3RaV4QBVNLdQOCxz9Q,1761
2
+ wexa_sdk/agentflows.py,sha256=3BqBElG7pDlcnaVjTAc4OZ9ZkeJoaSWVIvDWS5PBKJ8,5170
3
+ wexa_sdk/analytics.py,sha256=PVWvRvuyxOAbwxxAoILquESyb2Jw3cJGogbBu2ATRH4,345
4
+ wexa_sdk/connectors_mgmt.py,sha256=7vCT4ilez2YjZnfFEzb0raglTwweqxomqtJOb36RVT4,2229
5
+ wexa_sdk/executions.py,sha256=FzmYFPXLLbRSH--OPe44oaLYhngjc1xFBy_8hyx9iew,2129
6
+ wexa_sdk/files.py,sha256=Gujj7Oc8-TcoKFjpgT2GkhgJ28HBerv8Q9jlTfO_LOk,1276
7
+ wexa_sdk/inbox.py,sha256=KTlJPrr7v90n-1mQcPAazFeISSZ_m4bIPSXOpd3ra-0,3688
8
+ wexa_sdk/llm.py,sha256=7eiGb43PwIJqv9KczAdStYWNTFl-fe1etZ7R77kESBo,1009
9
+ wexa_sdk/marketplace.py,sha256=jhpeSWLU_BLFJcbwx_vcLeUff3UEPkylyhVIm_Fcb5Y,1609
10
+ wexa_sdk/project_members.py,sha256=TssDroghwYMsPRibZ1dNvKynzMtgIUN1TDFsmc2AuW4,514
11
+ wexa_sdk/projects.py,sha256=7mjDVlY7AUzG_zf2Gs_kFodEIWRcvLYoAiCsvpWp7-s,4266
12
+ wexa_sdk/schedules.py,sha256=IO7KolBX-8LddDCI7RqXlMHdrpDEVpEwn28FeMwb6TA,2543
13
+ wexa_sdk/settings.py,sha256=TDnqYeFBvrrj9kznTUTJxG29sJ02bHxBlqE55DgIfTg,290
14
+ wexa_sdk/skills.py,sha256=ZOFdspJsns_X88nBMrhaFWWcTKuh5afk0hK73-Qzs9M,1799
15
+ wexa_sdk/tables.py,sha256=meFX4Kg1Z-O2tqmrtvYEQlKozdmH8wQnZRBqktu_fWM,7326
16
+ wexa_sdk/tasks.py,sha256=DMoKUy_p7ADTK1da0vehTFGrjF3otnOC3YM9nMruf_0,1365
17
+ wexa_sdk/connectors/__init__.py,sha256=eEtSQ8oXlYnoD3CUxPCF3Il6bgGyN6zfKI3_Pu41jXo,51
18
+ wexa_sdk/connectors/core.py,sha256=7ghbFAPR1emgBL9PWStDp6AgZ5JDkiBWXQGaZ0B5Fek,1978
19
+ wexa_sdk/connectors/google_drive.py,sha256=Dibfoy8uKcGygmjCnrolPhKq5Fy5ye5-ee_kI4XD7kY,945
20
+ wexa_sdk/core/__init__.py,sha256=K-bUeDdR3iblN9BNwMQ-ZMbOcAJSAUkNwuoVz5L3fC4,39
21
+ wexa_sdk/core/http.py,sha256=-zKSZoXKoO50uE6W9lAiVD7cmrvloAll-YtMBGXKE8k,3722
22
+ wexa_sdk/models/__init__.py,sha256=yxQUjah8RTJh531y8QQXEeh5dp0d5k55dsrVvR4O1N8,75
23
+ wexa_sdk-0.1.11.dist-info/METADATA,sha256=uKdpm2XWoUPaUN6MhgPJsErrfO_J6tMdd1X1TQ9Xj2U,582
24
+ wexa_sdk-0.1.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
+ wexa_sdk-0.1.11.dist-info/top_level.txt,sha256=iXL6c0Kro-mkIoNbjT76txRuoilWB-P7AHhmvKtdXkA,9
26
+ wexa_sdk-0.1.11.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- wexa_sdk/__init__.py,sha256=dNvZ0fU4v0clBOuZWdd02SLxVfrBF2Ig-efQMOweq-I,1627
2
- wexa_sdk/agentflows.py,sha256=_1DJYXVi6Jyj9-jn0UtO1C7X_sdTcfdp0qdMtQZPQKo,2543
3
- wexa_sdk/analytics.py,sha256=PVWvRvuyxOAbwxxAoILquESyb2Jw3cJGogbBu2ATRH4,345
4
- wexa_sdk/connectors_mgmt.py,sha256=7vCT4ilez2YjZnfFEzb0raglTwweqxomqtJOb36RVT4,2229
5
- wexa_sdk/executions.py,sha256=-bVXo7ESA8AYZtc-tPDi28qWKJg_eYEQCo1kSYgE1P8,2509
6
- wexa_sdk/files.py,sha256=K2m6gxs_8XDhlW4W08DhSc-HwrMx3r2S5dOFvltLTqc,1235
7
- wexa_sdk/inbox.py,sha256=aKFmST2FX09mDYXyrqNpUYTsq6z0iJG4qtsCeMWKPdo,1957
8
- wexa_sdk/marketplace.py,sha256=jhpeSWLU_BLFJcbwx_vcLeUff3UEPkylyhVIm_Fcb5Y,1609
9
- wexa_sdk/project_members.py,sha256=TssDroghwYMsPRibZ1dNvKynzMtgIUN1TDFsmc2AuW4,514
10
- wexa_sdk/projects.py,sha256=zx5ncmhxbwSiFKU0QpVA01eKNxQW2zPYzPV_njGUCrc,2937
11
- wexa_sdk/settings.py,sha256=TDnqYeFBvrrj9kznTUTJxG29sJ02bHxBlqE55DgIfTg,290
12
- wexa_sdk/skills.py,sha256=ZOFdspJsns_X88nBMrhaFWWcTKuh5afk0hK73-Qzs9M,1799
13
- wexa_sdk/tables.py,sha256=JYGK1zWRWZgZGbiRyv-ko3XKdpxZv0piwb02h0v5ODw,2753
14
- wexa_sdk/tasks.py,sha256=YRXMxQwgpiflAxyUqedyo4LB6bsO7zsiM5uP0uE8_Ds,1349
15
- wexa_sdk/connectors/__init__.py,sha256=eEtSQ8oXlYnoD3CUxPCF3Il6bgGyN6zfKI3_Pu41jXo,51
16
- wexa_sdk/connectors/core.py,sha256=4dFp0CVCysUsIOT7l9V7bzXu1jRx9EBGZv2_bQkKPsQ,1188
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
20
- wexa_sdk/models/__init__.py,sha256=yxQUjah8RTJh531y8QQXEeh5dp0d5k55dsrVvR4O1N8,75
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,,