wexa-sdk 0.1.3__tar.gz → 0.1.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/PKG-INFO +1 -1
  2. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/pyproject.toml +1 -1
  3. wexa_sdk-0.1.7/tests/test_agentflows_add_skilled_agent.py +46 -0
  4. wexa_sdk-0.1.7/tests/test_agentflows_update_skilled_live.py +46 -0
  5. wexa_sdk-0.1.7/tests/test_agentflows_update_skilled_unit.py +43 -0
  6. wexa_sdk-0.1.7/tests/test_connectors_config.py +30 -0
  7. wexa_sdk-0.1.7/tests/test_connectors_config_live.py +58 -0
  8. wexa_sdk-0.1.7/tests/test_projects_create.py +60 -0
  9. wexa_sdk-0.1.7/tests/test_projects_create_live.py +41 -0
  10. wexa_sdk-0.1.7/tests/test_projects_get_all_live.py +51 -0
  11. wexa_sdk-0.1.7/tests/test_projects_mcpconnectortesting.py +25 -0
  12. wexa_sdk-0.1.7/tests/test_projects_mcpconnectortesting_live.py +26 -0
  13. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_tables.py +2 -2
  14. wexa_sdk-0.1.7/tests/test_tables_create_live.py +36 -0
  15. wexa_sdk-0.1.7/tests/test_tables_create_rich.py +54 -0
  16. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/agentflows.py +47 -0
  17. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/connectors/core.py +31 -3
  18. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/core/http.py +1 -1
  19. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/projects.py +14 -5
  20. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/tables.py +58 -2
  21. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk.egg-info/PKG-INFO +1 -1
  22. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk.egg-info/SOURCES.txt +12 -0
  23. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/README.md +0 -0
  24. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/setup.cfg +0 -0
  25. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_agentflows.py +0 -0
  26. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_agentflows_crud.py +0 -0
  27. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_analytics.py +0 -0
  28. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_connectors_core.py +0 -0
  29. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_connectors_mgmt.py +0 -0
  30. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_demo_coworker_workflow.py +0 -0
  31. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_executions.py +0 -0
  32. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_files.py +0 -0
  33. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_inbox.py +0 -0
  34. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_integration_agentflows_create.py +0 -0
  35. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_integration_marketplace.py +0 -0
  36. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_marketplace.py +0 -0
  37. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_project_members.py +0 -0
  38. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_projects.py +0 -0
  39. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_projects_crud.py +0 -0
  40. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_projects_get_all.py +0 -0
  41. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_settings.py +0 -0
  42. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_skills.py +0 -0
  43. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/tests/test_tasks.py +0 -0
  44. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/__init__.py +0 -0
  45. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/analytics.py +0 -0
  46. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/connectors/__init__.py +0 -0
  47. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/connectors/google_drive.py +0 -0
  48. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/connectors_mgmt.py +0 -0
  49. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/core/__init__.py +0 -0
  50. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/executions.py +0 -0
  51. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/files.py +0 -0
  52. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/inbox.py +0 -0
  53. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/marketplace.py +0 -0
  54. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/models/__init__.py +0 -0
  55. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/project_members.py +0 -0
  56. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/settings.py +0 -0
  57. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/skills.py +0 -0
  58. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk/tasks.py +0 -0
  59. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk.egg-info/dependency_links.txt +0 -0
  60. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk.egg-info/requires.txt +0 -0
  61. {wexa_sdk-0.1.3 → wexa_sdk-0.1.7}/wexa_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wexa-sdk
3
- Version: 0.1.3
3
+ Version: 0.1.7
4
4
  Summary: Official Wexa Python SDK
5
5
  Author: Wexa
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wexa-sdk"
7
- version = "0.1.3"
7
+ version = "0.1.7"
8
8
  description = "Official Wexa Python SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,46 @@
1
+ from wexa_sdk import WexaClient
2
+
3
+
4
+ def test_agentflows_add_skilled_agent_requests(monkeypatch):
5
+ client = WexaClient(base_url="https://api.wexa.ai", api_key="key")
6
+
7
+ captured = {}
8
+
9
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
10
+ captured["method"] = method
11
+ captured["path"] = path
12
+ captured["params"] = params
13
+ captured["json"] = json
14
+ return {"ok": True}
15
+
16
+ client.http.request = fake_request # type: ignore
17
+
18
+ body = {
19
+ "_id": "agent-1",
20
+ "role": "assistant",
21
+ "title": "Writer",
22
+ "skills": ["content_writer"],
23
+ "context": [],
24
+ "llm": {"model": "gpt-4o", "max_tokens": 512, "temperature": 0.7},
25
+ "memory": {"memory_type": "short_term"},
26
+ "agent_type": "skilled",
27
+ "role_description": "Creates content",
28
+ "prompt": {"template": "...", "variables": [], "display_template": "..."},
29
+ "triggers": [],
30
+ "has_knowledge_base": False,
31
+ "is_user_specific_task": False,
32
+ "is_preview_mode_enabled": False,
33
+ "conditions": [],
34
+ }
35
+
36
+ resp = client.agentflows.add_skilled_agent(
37
+ "aflow-123",
38
+ projectID="proj-1",
39
+ body=body,
40
+ )
41
+
42
+ assert captured["method"] == "POST"
43
+ assert captured["path"] == "/agentflow/aflow-123/skilled"
44
+ assert captured["params"] == {"projectID": "proj-1"}
45
+ assert captured["json"] == body
46
+ assert resp == {"ok": True}
@@ -0,0 +1,46 @@
1
+ import os
2
+ import json
3
+ import pytest
4
+ from wexa_sdk import WexaClient
5
+
6
+ BASE_URL = os.getenv("WEXA_BASE_URL", "https://api.wexa.ai")
7
+ API_KEY = os.getenv("WEXA_API_KEY") or ""
8
+ PROJECT_ID = os.getenv("PROJECT_ID", "68c3b68b9120a0bdfb281d89")
9
+ AGENTFLOW_ID = os.getenv("AGENTFLOW_ID", "6901def632348b1fae4c0e6a")
10
+ AGENT_ID = os.getenv("AGENT_ID", "6901def7e643a25d472d0952")
11
+
12
+ # The user-provided payload (trimmed to minimal required where possible)
13
+ PAYLOAD = {
14
+ "role": "Create Call",
15
+ "title": "Create a Call",
16
+ "skills": ["6901dbf973f8d927fe126fad"],
17
+ "prompt": {
18
+ "display_template": "...", # large rich text omitted for brevity
19
+ "variables": [],
20
+ "template": "..."
21
+ },
22
+ "context": [],
23
+ "triggers": [],
24
+ "llm": {"model": "bedrock/eu.anthropic.claude-3-7-sonnet-20250219-v1:0", "max_tokens": 10000, "temperature": 0},
25
+ "role_description": "Creating a calling agent",
26
+ "memory": {"memory_type": "lt"},
27
+ "has_knowledge_base": False,
28
+ "is_user_specific_task": False,
29
+ "is_preview_mode_enabled": False,
30
+ }
31
+
32
+
33
+ @pytest.mark.integration
34
+ def test_update_skilled_agent_live():
35
+ if not API_KEY:
36
+ pytest.skip("Set WEXA_API_KEY to run this live test")
37
+
38
+ client = WexaClient(base_url=BASE_URL, api_key=API_KEY)
39
+ resp = client.agentflows.update_skilled_agent(
40
+ agentflow_id=AGENTFLOW_ID,
41
+ agent_id=AGENT_ID,
42
+ projectID=PROJECT_ID,
43
+ body=PAYLOAD,
44
+ )
45
+ print(json.dumps(resp, indent=2))
46
+ assert isinstance(resp, dict) or isinstance(resp, list)
@@ -0,0 +1,43 @@
1
+ from wexa_sdk import WexaClient
2
+
3
+
4
+ def test_agentflows_update_skilled_builds_request(monkeypatch):
5
+ c = WexaClient(base_url="https://api.wexa.ai", api_key="key")
6
+ captured = {}
7
+
8
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
9
+ captured["method"] = method
10
+ captured["path"] = path
11
+ captured["params"] = params
12
+ captured["json"] = json
13
+ return {"ok": True}
14
+
15
+ c.http.request = fake_request # type: ignore
16
+
17
+ body = {
18
+ "role": "assistant",
19
+ "title": "Create a Call",
20
+ "skills": ["skill-1"],
21
+ "prompt": {"template": "t", "variables": [], "display_template": "d"},
22
+ "context": [],
23
+ "triggers": [],
24
+ "llm": {"model": "m", "max_tokens": 1000, "temperature": 0},
25
+ "role_description": "desc",
26
+ "memory": {"memory_type": "lt"},
27
+ "has_knowledge_base": False,
28
+ "is_user_specific_task": False,
29
+ "is_preview_mode_enabled": False,
30
+ }
31
+
32
+ resp = c.agentflows.update_skilled_agent(
33
+ agentflow_id="aflow-1",
34
+ agent_id="agent-1",
35
+ projectID="proj-1",
36
+ body=body,
37
+ )
38
+
39
+ assert captured["method"] == "POST"
40
+ assert captured["path"] == "/agentflow/aflow-1/update/skilled/agent-1"
41
+ assert captured["params"] == {"projectID": "proj-1"}
42
+ assert captured["json"] == body
43
+ assert resp == {"ok": True}
@@ -0,0 +1,30 @@
1
+ from wexa_sdk import WexaClient
2
+
3
+
4
+ def test_connectors_config_endpoints(monkeypatch):
5
+ c = WexaClient(base_url="https://api.wexa.ai", api_key="key")
6
+ calls = []
7
+
8
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
9
+ calls.append((method, path, params, json))
10
+ return {"ok": True}
11
+
12
+ c.http.request = fake_request # type: ignore
13
+
14
+ # set config -> POST /actions/{CATEGORY}/config?projectID=...
15
+ c.connectors.set_config("content_creator", "p1", {"enabled": True})
16
+ # get config -> GET /actions/{CATEGORY}/config/{projectID}
17
+ c.connectors.get_config("content_creator", "p1")
18
+
19
+ assert calls[0] == (
20
+ "POST",
21
+ "/actions/content_creator/config",
22
+ {"projectID": "p1"},
23
+ {"enabled": True},
24
+ )
25
+ assert calls[1] == (
26
+ "GET",
27
+ "/actions/content_creator/config/p1",
28
+ None,
29
+ None,
30
+ )
@@ -0,0 +1,58 @@
1
+ import os
2
+ import json
3
+ import time
4
+ import pytest
5
+ from wexa_sdk import WexaClient
6
+
7
+ BASE_URL = os.getenv("WEXA_BASE_URL", "https://api.wexa.ai")
8
+ API_KEY = os.getenv("WEXA_API_KEY")
9
+ PROJECT_ID = "68c3b68b9120a0bdfb281d89" # provided
10
+ CATEGORY = "content_creator"
11
+ ORG_ID = os.getenv("WEXA_ORG_ID") or os.getenv("NEXT_PUBLIC_ORG_ID")
12
+
13
+
14
+ @pytest.mark.integration
15
+ def test_content_creator_config_live_get_then_set():
16
+ """
17
+ Live test for connector config:
18
+ 1) GET /actions/{CATEGORY}/config/{projectID}
19
+ 2) POST /actions/{CATEGORY}/config?projectID=...
20
+
21
+ Skips when WEXA_API_KEY or ORG_ID (WEXA_ORG_ID/NEXT_PUBLIC_ORG_ID) is not set.
22
+ """
23
+ if not API_KEY:
24
+ pytest.skip("Set WEXA_API_KEY in the environment to run this live test")
25
+ if not ORG_ID:
26
+ pytest.skip("Set WEXA_ORG_ID or NEXT_PUBLIC_ORG_ID to run this live test")
27
+
28
+ client = WexaClient(base_url=BASE_URL, api_key=API_KEY)
29
+
30
+ # Step 1: GET current config
31
+ current = client.connectors.get_config(CATEGORY, PROJECT_ID)
32
+ print(json.dumps({"current": current}, indent=2))
33
+
34
+ # Step 2: Build required payload from GET response and env
35
+ cfg = (current or {}).get("config", {})
36
+ # Ensure unique name to avoid 'Connector with the same name already exists'
37
+ base_name = cfg.get("name") or "Content creator"
38
+ unique_name = f"{base_name}-{int(time.time())}"
39
+ update_body = {
40
+ # required by backend
41
+ "name": unique_name,
42
+ "description": cfg.get("description") or "",
43
+ "category": CATEGORY,
44
+ "org_id": ORG_ID,
45
+ "projectID": PROJECT_ID,
46
+ "logo": cfg.get("logo") or "",
47
+ "ui_form": cfg.get("ui_form") or [],
48
+ # optional but present in GET; include to be safe
49
+ "config": cfg.get("config") or {},
50
+ # toggle
51
+ "enabled": True,
52
+ }
53
+
54
+ updated = client.connectors.set_config(CATEGORY, PROJECT_ID, update_body)
55
+ print(json.dumps({"updated": updated}, indent=2))
56
+
57
+ # Basic assertions
58
+ assert isinstance(updated, dict)
@@ -0,0 +1,60 @@
1
+ from wexa_sdk import WexaClient
2
+
3
+
4
+ def test_projects_create_published(monkeypatch):
5
+ c = WexaClient(base_url="https://api.wexa.ai", api_key="key")
6
+ calls = []
7
+
8
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
9
+ calls.append((method, path, params, json))
10
+ return {"_id": "p123"}
11
+
12
+ c.http.request = fake_request # type: ignore
13
+
14
+ body = {
15
+ "orgId": "o1",
16
+ "projectName": "My Project",
17
+ "description": "desc",
18
+ "coworker_role": "role",
19
+ "status": "published",
20
+ }
21
+
22
+ res = c.projects.create(body)
23
+
24
+ # Assert method, path, and payload
25
+ assert calls[0][0] == "POST"
26
+ assert calls[0][1] == "/v1/project"
27
+ assert calls[0][2] is None # params
28
+ assert calls[0][3] == body # json body
29
+ assert res == {"_id": "p123"}
30
+
31
+
32
+ def test_projects_create_simple_published(monkeypatch):
33
+ c = WexaClient(base_url="https://api.wexa.ai", api_key="key")
34
+ calls = []
35
+
36
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
37
+ calls.append((method, path, params, json))
38
+ return {"_id": "p456"}
39
+
40
+ c.http.request = fake_request # type: ignore
41
+
42
+ res = c.projects.create_simple(
43
+ orgId="o1",
44
+ projectName="Simple Project",
45
+ description="desc2",
46
+ coworker_role="role2",
47
+ status="published",
48
+ )
49
+
50
+ assert calls[0][0] == "POST"
51
+ assert calls[0][1] == "/v1/project"
52
+ assert calls[0][2] is None
53
+ assert calls[0][3] == {
54
+ "orgId": "o1",
55
+ "projectName": "Simple Project",
56
+ "description": "desc2",
57
+ "coworker_role": "role2",
58
+ "status": "published",
59
+ }
60
+ assert res == {"_id": "p456"}
@@ -0,0 +1,41 @@
1
+ import time
2
+ import json
3
+ import pytest
4
+ from wexa_sdk import WexaClient
5
+
6
+ # ===== Configure for live run =====
7
+ BASE_URL = "https://api.wexa.ai"
8
+ API_KEY = "506af7e9-9309-4fdf-b004-8d4cb09dfc05"
9
+ ORG_ID = "66f3cdde22bc63eb7490e23e"
10
+
11
+
12
+ @pytest.mark.integration
13
+ def test_projects_create_live_published():
14
+ """
15
+ Live test that creates a new project in the given org with status "published".
16
+ This will mutate production data.
17
+
18
+ To run:
19
+ pytest -s tests/test_projects_create_live.py
20
+ """
21
+ if not API_KEY or "REPLACE_WITH" in API_KEY or not ORG_ID:
22
+ pytest.skip("Edit BASE_URL, API_KEY, ORG_ID before running this live test")
23
+
24
+ c = WexaClient(base_url=BASE_URL, api_key=API_KEY)
25
+
26
+ name = f"SDK Create Test {int(time.time())}"
27
+ body = {
28
+ "orgId": ORG_ID,
29
+ "projectName": name,
30
+ "description": "Created via live integration test",
31
+ "coworker_role": "Tester",
32
+ "status": "published",
33
+ }
34
+ res = c.projects.create(body)
35
+
36
+ print(json.dumps(res, indent=2))
37
+
38
+ assert isinstance(res, dict)
39
+ created = res.get("data", res)
40
+ assert isinstance(created, dict)
41
+ assert "_id" in created
@@ -0,0 +1,51 @@
1
+ import pytest
2
+ import json
3
+ from wexa_sdk import WexaClient
4
+
5
+ # ===== Configure these before running =====
6
+ # Use prod or local API base URL
7
+ BASE_URL = "https://api.wexa.ai" # or "http://localhost:5000"
8
+ API_KEY = "506af7e9-9309-4fdf-b004-8d4cb09dfc05"
9
+ TEST_USER_ID = "66f3cdde22bc63eb7490e23c"
10
+ TEST_ORG_ID = "66f3cdde22bc63eb7490e23e"
11
+
12
+
13
+ @pytest.mark.integration
14
+ def test_projects_get_all_live():
15
+ """
16
+ Live integration test for Projects.get_all() against a real API.
17
+
18
+ Usage:
19
+ 1) Edit BASE_URL, API_KEY, TEST_USER_ID, TEST_ORG_ID at the top of this file.
20
+ 2) Run: pytest -q tests/test_projects_get_all_live.py
21
+ """
22
+ base_url = BASE_URL
23
+ api_key = API_KEY
24
+ user_id = TEST_USER_ID
25
+ org_id = TEST_ORG_ID
26
+
27
+ if (not api_key) or ("REPLACE_WITH_API_KEY" in api_key) or (not user_id) or (not org_id):
28
+ pytest.skip("Edit BASE_URL, API_KEY, TEST_USER_ID, TEST_ORG_ID in this file to run this live test")
29
+
30
+ c = WexaClient(base_url=base_url, api_key=api_key)
31
+ res = c.projects.get_all(
32
+ status="published",
33
+ user_id=user_id,
34
+ org_id=org_id,
35
+ page=1,
36
+ limit=12,
37
+ )
38
+
39
+ # Print the response for inspection
40
+ print(json.dumps(res, indent=2))
41
+
42
+ assert isinstance(res, dict)
43
+
44
+ # Backend may return either a list wrapper or items/count; accept both
45
+ if "projectList" in res:
46
+ assert isinstance(res["projectList"], list)
47
+ assert "totalCount" in res
48
+ else:
49
+ # fallback shape if API differs
50
+ assert "items" in res
51
+ assert isinstance(res["items"], list)
@@ -0,0 +1,25 @@
1
+ import pytest
2
+ from wexa_sdk import WexaClient
3
+
4
+
5
+ def test_projects_get_by_project_name_builds_correct_request(monkeypatch):
6
+ captured = {}
7
+
8
+ def fake_request(method, path, *, params=None, json=None, headers=None):
9
+ captured["method"] = method
10
+ captured["path"] = path
11
+ captured["params"] = params
12
+ captured["json"] = json
13
+ captured["headers"] = headers
14
+ return {"ok": True}
15
+
16
+ c = WexaClient(base_url="https://testing.api.wexa.ai", api_key="265d37d8-3496-4758-b0d5-dc64cc29b444")
17
+ monkeypatch.setattr(c.projects.http, "request", fake_request)
18
+
19
+ res = c.projects.get_by_project_name("Wexa Recruit")
20
+
21
+ assert captured["method"] == "GET"
22
+ assert captured["path"] == "/project/projectName/Wexa%20Recruit"
23
+ assert res == {"ok": True}
24
+
25
+
@@ -0,0 +1,26 @@
1
+ import os
2
+ import json
3
+ import pytest
4
+ from wexa_sdk import WexaClient
5
+
6
+
7
+ # Live test uses provided base URL and API key
8
+ BASE_URL = "https://testing.api.wexa.ai"
9
+ API_KEY = "265d37d8-3496-4758-b0d5-dc64cc29b444"
10
+
11
+
12
+ @pytest.mark.integration
13
+ def test_projects_mcp_connector_testing_live():
14
+ project_name = os.getenv("WEXA_TEST_PROJECT_NAME")
15
+ if not project_name:
16
+ pytest.skip("Set WEXA_TEST_PROJECT_NAME to run this live test")
17
+
18
+ c = WexaClient(base_url=BASE_URL, api_key=API_KEY)
19
+ res = c.projects.get_by_project_name(project_name)
20
+
21
+ # Print for visibility when running live
22
+ print(json.dumps(res, indent=2))
23
+
24
+ assert res is not None
25
+
26
+
@@ -12,7 +12,7 @@ def test_tables_endpoints(monkeypatch):
12
12
  c.http.request = fake_request # type: ignore
13
13
 
14
14
  # Tables
15
- c.tables.create_table("proj", {"name": "T"})
15
+ c.tables.create_table("proj", {"table_name": "T"})
16
16
  c.tables.list_tables("proj")
17
17
  c.tables.get_table("proj", "tbl")
18
18
  c.tables.get_table_view("tbl")
@@ -32,7 +32,7 @@ def test_tables_endpoints(monkeypatch):
32
32
  c.tables.bulk_upsert("proj", "tbl", [{"a": 1}])
33
33
  c.tables.export("proj", "tbl")
34
34
 
35
- assert calls[0] == ("POST", "/create/table", {"projectID": "proj"}, {"projectID": "proj", "name": "T"})
35
+ assert calls[0] == ("POST", "/create/table", {"projectID": "proj"}, {"projectID": "proj", "table_name": "T"})
36
36
  assert calls[1] == ("GET", "/storage/proj", None, None)
37
37
  assert calls[2] == ("GET", "/storage/proj/tbl", None, None)
38
38
  assert calls[3] == ("GET", "/table/view/tbl", None, None)
@@ -0,0 +1,36 @@
1
+ import os
2
+ import json
3
+ import pytest
4
+ from wexa_sdk import WexaClient
5
+
6
+ BASE_URL = os.getenv("WEXA_BASE_URL", "https://api.wexa.ai")
7
+ API_KEY = os.getenv("WEXA_API_KEY")
8
+ PROJECT_ID = "68c3b68b9120a0bdfb281d89" # provided
9
+
10
+
11
+ @pytest.mark.integration
12
+ def test_tables_create_live_wexa_sdk():
13
+ """
14
+ Live test to create a table with random columns (no triggers).
15
+ Skips if WEXA_API_KEY is not set.
16
+ """
17
+ if not API_KEY:
18
+ pytest.skip("Set WEXA_API_KEY in your environment to run this live test")
19
+
20
+ client = WexaClient(base_url=BASE_URL, api_key=API_KEY)
21
+
22
+ table_name = "wexa_sdk"
23
+ spec = {
24
+ "table_name": table_name,
25
+ "columns": [
26
+ {"column_name": "email", "column_type": "String", "column_id": "email"},
27
+ {"column_name": "age", "column_type": "Number", "column_id": "age"},
28
+ {"column_name": "tags", "column_type": "Array", "column_id": "tags", "array_type": "String", "default_value": []},
29
+ {"column_name": "status", "column_type": "Enum", "column_id": "status", "enum_options": ["active", "inactive"], "default_value": "active"},
30
+ ],
31
+ }
32
+
33
+ res = client.tables.create_table(PROJECT_ID, spec)
34
+ print(json.dumps(res, indent=2))
35
+
36
+ assert isinstance(res, dict)
@@ -0,0 +1,54 @@
1
+ from wexa_sdk import WexaClient
2
+
3
+
4
+ def test_create_table_rich_spec(monkeypatch):
5
+ c = WexaClient(base_url="https://api.wexa.ai", api_key="key")
6
+ calls = []
7
+
8
+ def fake_request(method, path, *, params=None, json=None, headers=None): # type: ignore
9
+ calls.append((method, path, params, json))
10
+ return {"ok": True}
11
+
12
+ c.http.request = fake_request # type: ignore
13
+
14
+ spec = {
15
+ "table_name": "Customers",
16
+ "columns": [
17
+ {"column_name": "email", "column_type": "String", "column_id": "email"},
18
+ {
19
+ "column_name": "tags",
20
+ "column_type": "Array",
21
+ "column_id": "tags",
22
+ "array_type": "String",
23
+ "default_value": [],
24
+ },
25
+ {
26
+ "column_name": "status",
27
+ "column_type": "Enum",
28
+ "column_id": "status",
29
+ "enum_options": ["active", "inactive"],
30
+ "default_value": "active",
31
+ },
32
+ {
33
+ "column_name": "profile",
34
+ "column_type": "Object",
35
+ "column_id": "profile",
36
+ "object_fields": [{"key": "firstName", "keyType": "String"}],
37
+ },
38
+ ],
39
+ "triggers": [
40
+ {"goal": "audit new row", "event": "row_created", "trigger_type": "coworker"}
41
+ ],
42
+ }
43
+
44
+ project_id = "proj123"
45
+ c.tables.create_table(project_id, spec)
46
+
47
+ # Assertions
48
+ assert calls[0][0] == "POST"
49
+ assert calls[0][1] == "/create/table"
50
+ assert calls[0][2] == {"projectID": project_id}
51
+ body = calls[0][3]
52
+ assert body["projectID"] == project_id
53
+ assert body["table_name"] == "Customers"
54
+ assert isinstance(body.get("columns"), list)
@@ -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,49 @@ 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
+ # Typed body for updating a skilled agent to enable IDE suggestions
79
+ class UpdateSkilledAgentBody(TypedDict, total=False):
80
+ """Body for updating a skilled agent within an AgentFlow.
81
+
82
+ Required (per API docs):
83
+ - role: str
84
+ - title: str
85
+ - skills: list[str]
86
+ - prompt: { template: str, variables: list, display_template: str }
87
+ - context: list
88
+ - triggers: list
89
+ - llm: { model: str, max_tokens: int, temperature: int }
90
+ - role_description: str
91
+ - memory: { memory_type: str }
92
+ - has_knowledge_base: bool
93
+ - is_user_specific_task: bool
94
+ - is_preview_mode_enabled: bool
95
+ """
96
+ role: str
97
+ title: str
98
+ skills: list[str]
99
+ prompt: dict
100
+ context: list
101
+ triggers: list
102
+ llm: dict
103
+ role_description: str
104
+ memory: dict
105
+ has_knowledge_base: bool
106
+ is_user_specific_task: bool
107
+ is_preview_mode_enabled: bool
108
+
109
+ def update_skilled_agent(self, agentflow_id: str, agent_id: str, *, projectID: str, body: UpdateSkilledAgentBody | dict):
110
+ """Update a skilled agent within an AgentFlow.
111
+
112
+ Endpoint: POST /agentflow/{agentflow_id}/update/skilled/{agent_id}?projectID=...
113
+
114
+ Args:
115
+ agentflow_id: ID of the AgentFlow
116
+ agent_id: ID of the skilled agent inside the AgentFlow
117
+ projectID: Project ID (query param, required)
118
+ body: Update payload, see UpdateSkilledAgentBody for suggested keys
119
+ """
120
+ params = {"projectID": projectID} if projectID else None
121
+ path = f"/agentflow/{agentflow_id}/update/skilled/{agent_id}"
122
+ 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:
@@ -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.7",
41
41
  }
42
42
  if self.user_agent:
43
43
  hdrs["User-Agent"] = self.user_agent
@@ -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
@@ -132,3 +132,12 @@ class Projects:
132
132
 
133
133
  def delete(self, project_id: str):
134
134
  return self.http.request("DELETE", f"/v1/project/{project_id}")
135
+
136
+ def get_by_project_name(self, project_name: str):
137
+ """
138
+ Get project by projectName.
139
+
140
+ GET /project/projectName/{projectName}
141
+ """
142
+ safe = quote(project_name, safe="")
143
+ return self.http.request("GET", f"/project/projectName/{safe}")
@@ -1,14 +1,70 @@
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}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wexa-sdk
3
- Version: 0.1.3
3
+ Version: 0.1.7
4
4
  Summary: Official Wexa Python SDK
5
5
  Author: Wexa
6
6
  License: Apache-2.0
@@ -1,8 +1,13 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  tests/test_agentflows.py
4
+ tests/test_agentflows_add_skilled_agent.py
4
5
  tests/test_agentflows_crud.py
6
+ tests/test_agentflows_update_skilled_live.py
7
+ tests/test_agentflows_update_skilled_unit.py
5
8
  tests/test_analytics.py
9
+ tests/test_connectors_config.py
10
+ tests/test_connectors_config_live.py
6
11
  tests/test_connectors_core.py
7
12
  tests/test_connectors_mgmt.py
8
13
  tests/test_demo_coworker_workflow.py
@@ -14,11 +19,18 @@ tests/test_integration_marketplace.py
14
19
  tests/test_marketplace.py
15
20
  tests/test_project_members.py
16
21
  tests/test_projects.py
22
+ tests/test_projects_create.py
23
+ tests/test_projects_create_live.py
17
24
  tests/test_projects_crud.py
18
25
  tests/test_projects_get_all.py
26
+ tests/test_projects_get_all_live.py
27
+ tests/test_projects_mcpconnectortesting.py
28
+ tests/test_projects_mcpconnectortesting_live.py
19
29
  tests/test_settings.py
20
30
  tests/test_skills.py
21
31
  tests/test_tables.py
32
+ tests/test_tables_create_live.py
33
+ tests/test_tables_create_rich.py
22
34
  tests/test_tasks.py
23
35
  wexa_sdk/__init__.py
24
36
  wexa_sdk/agentflows.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes