alita-sdk 0.3.162__py3-none-any.whl → 0.3.164__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.
Files changed (27) hide show
  1. alita_sdk/runtime/langchain/assistant.py +2 -2
  2. alita_sdk/runtime/langchain/store_manager.py +22 -1
  3. alita_sdk/runtime/toolkits/tools.py +1 -1
  4. alita_sdk/tools/__init__.py +7 -1
  5. alita_sdk/tools/carrier/api_wrapper.py +76 -4
  6. alita_sdk/tools/carrier/backend_reports_tool.py +31 -12
  7. alita_sdk/tools/carrier/backend_tests_tool.py +14 -8
  8. alita_sdk/tools/carrier/cancel_ui_test_tool.py +178 -0
  9. alita_sdk/tools/carrier/carrier_sdk.py +99 -15
  10. alita_sdk/tools/carrier/create_ui_excel_report_tool.py +473 -0
  11. alita_sdk/tools/carrier/create_ui_test_tool.py +199 -0
  12. alita_sdk/tools/carrier/lighthouse_excel_reporter.py +155 -0
  13. alita_sdk/tools/carrier/run_ui_test_tool.py +394 -0
  14. alita_sdk/tools/carrier/tools.py +11 -1
  15. alita_sdk/tools/carrier/ui_reports_tool.py +6 -2
  16. alita_sdk/tools/carrier/update_ui_test_schedule_tool.py +278 -0
  17. alita_sdk/tools/memory/__init__.py +7 -0
  18. alita_sdk/tools/postman/__init__.py +7 -0
  19. alita_sdk/tools/postman/api_wrapper.py +335 -0
  20. alita_sdk/tools/zephyr_squad/__init__.py +62 -0
  21. alita_sdk/tools/zephyr_squad/api_wrapper.py +135 -0
  22. alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py +79 -0
  23. {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.164.dist-info}/METADATA +4 -3
  24. {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.164.dist-info}/RECORD +27 -18
  25. {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.164.dist-info}/WHEEL +0 -0
  26. {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.164.dist-info}/licenses/LICENSE +0 -0
  27. {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.164.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,278 @@
1
+ import logging
2
+ import json
3
+ import traceback
4
+ import re
5
+ from typing import Type
6
+ from langchain_core.tools import BaseTool, ToolException
7
+ from pydantic.fields import Field
8
+ from pydantic import create_model, BaseModel
9
+ from .api_wrapper import CarrierAPIWrapper
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class UpdateUITestScheduleTool(BaseTool):
16
+ api_wrapper: CarrierAPIWrapper = Field(..., description="Carrier API Wrapper instance")
17
+ name: str = "update_ui_test_schedule"
18
+ description: str = ("Update UI test schedule on the Carrier platform. Use this tool when user wants to update, modify, or change a UI test schedule. "
19
+ "Provide test_id, schedule_name, and cron_timer, or leave empty to see available tests.")
20
+ args_schema: Type[BaseModel] = create_model(
21
+ "UpdateUITestScheduleInput",
22
+ test_id=(str, Field(default="", description="Test ID to update schedule for")),
23
+ schedule_name=(str, Field(default="", description="Name for the new schedule")),
24
+ cron_timer=(str, Field(default="", description="Cron expression for schedule timing (e.g., '0 2 * * *')")),
25
+ )
26
+
27
+ def _run(self, test_id: str = "", schedule_name: str = "", cron_timer: str = ""):
28
+ try:
29
+ # Check if no parameters provided - show available tests
30
+ if (not test_id or test_id.strip() == "") and (not schedule_name or schedule_name.strip() == "") and (not cron_timer or cron_timer.strip() == ""):
31
+ return self._show_available_tests_and_instructions()
32
+
33
+ # Check if test_id is missing but other params provided
34
+ if (not test_id or test_id.strip() == ""):
35
+ return self._show_missing_test_id_message()
36
+
37
+ # Check if schedule_name or cron_timer is missing
38
+ if (not schedule_name or schedule_name.strip() == "") or (not cron_timer or cron_timer.strip() == ""):
39
+ return self._show_missing_parameters_message(test_id, schedule_name, cron_timer)
40
+
41
+ # Validate cron timer format
42
+ if not self._validate_cron_timer(cron_timer):
43
+ return self._show_invalid_cron_message(cron_timer)
44
+
45
+ # Get UI tests list to verify test exists
46
+ ui_tests = self.api_wrapper.get_ui_tests_list()
47
+ test_data = None
48
+ test_id_int = None
49
+
50
+ # Try to find test by ID
51
+ if test_id.isdigit():
52
+ test_id_int = int(test_id)
53
+ for test in ui_tests:
54
+ if test.get("id") == test_id_int:
55
+ test_data = test
56
+ break
57
+
58
+ if not test_data:
59
+ available_tests = []
60
+ for test in ui_tests:
61
+ available_tests.append(f"ID: {test.get('id')}, Name: {test.get('name')}")
62
+
63
+ return f"❌ **Test not found for ID: {test_id}**\n\n**Available UI tests:**\n" + "\n".join([f"- {test}" for test in available_tests])
64
+
65
+ # Get detailed test configuration
66
+ test_details = self.api_wrapper.get_ui_test_details(str(test_id_int))
67
+
68
+ if not test_details:
69
+ return f"❌ **Could not retrieve test details for test ID {test_id_int}.**"
70
+
71
+ # Parse and update the test configuration
72
+ updated_config = self._parse_and_update_test_data(test_details, schedule_name, cron_timer)
73
+
74
+ # Execute the PUT request to update the test
75
+ result = self.api_wrapper.update_ui_test(str(test_id_int), updated_config)
76
+
77
+ return self._format_success_message(test_data.get('name', 'Unknown'), test_id_int, schedule_name, cron_timer)
78
+
79
+ except Exception:
80
+ stacktrace = traceback.format_exc()
81
+ logger.error(f"Error updating UI test schedule: {stacktrace}")
82
+ raise ToolException(stacktrace)
83
+
84
+ def _show_available_tests_and_instructions(self):
85
+ """Show available tests and instructions when no parameters provided."""
86
+ try:
87
+ ui_tests = self.api_wrapper.get_ui_tests_list()
88
+
89
+ if not ui_tests:
90
+ return "❌ **No UI tests found.**"
91
+
92
+ message = ["# 📋 Update UI Test Schedule\n"]
93
+ message.append("## Available UI Tests:")
94
+
95
+ for test in ui_tests:
96
+ message.append(f"- **ID: {test.get('id')}**, Name: `{test.get('name')}`, Runner: `{test.get('runner')}`")
97
+
98
+ message.append("\n## 📝 Instructions:")
99
+ message.append("For updating UI test schedule, please provide me:")
100
+ message.append("- **`test_id`** - The ID of the test you want to update")
101
+ message.append("- **`schedule_name`** - A name for your new schedule")
102
+ message.append("- **`cron_timer`** - Cron expression for timing (e.g., `0 2 * * *` for daily at 2 AM)")
103
+
104
+ message.append("\n## 💡 Example:")
105
+ message.append("```")
106
+ message.append("test_id: 42")
107
+ message.append("schedule_name: Daily Morning Test")
108
+ message.append("cron_timer: 0 2 * * *")
109
+ message.append("```")
110
+
111
+ return "\n".join(message)
112
+
113
+ except Exception:
114
+ stacktrace = traceback.format_exc()
115
+ logger.error(f"Error fetching UI tests list: {stacktrace}")
116
+ raise ToolException(stacktrace)
117
+
118
+ def _show_missing_test_id_message(self):
119
+ """Show message when test_id is missing."""
120
+ return """# ❌ Missing Test ID
121
+
122
+ **For updating UI test schedule, please provide me:**
123
+ - **`test_id`** - The ID of the test you want to update
124
+ - **`schedule_name`** - A name for your new schedule
125
+ - **`cron_timer`** - Cron expression for timing
126
+
127
+ Use the tool without parameters to see available tests."""
128
+
129
+ def _show_missing_parameters_message(self, test_id: str, schedule_name: str, cron_timer: str):
130
+ """Show message when some parameters are missing."""
131
+ missing = []
132
+ if not schedule_name or schedule_name.strip() == "":
133
+ missing.append("**`schedule_name`**")
134
+ if not cron_timer or cron_timer.strip() == "":
135
+ missing.append("**`cron_timer`**")
136
+
137
+ message = [f"# ❌ Missing Parameters for Test ID: {test_id}\n"]
138
+ message.append("**Missing parameters:**")
139
+ for param in missing:
140
+ message.append(f"- {param}")
141
+
142
+ message.append("\n**For updating UI test schedule, please provide:**")
143
+ message.append("- **`test_id`** ✅ (provided)")
144
+ message.append("- **`schedule_name`** - A name for your new schedule")
145
+ message.append("- **`cron_timer`** - Cron expression for timing (e.g., `0 2 * * *`)")
146
+
147
+ return "\n".join(message)
148
+
149
+ def _validate_cron_timer(self, cron_timer: str) -> bool:
150
+ """Validate cron timer format."""
151
+ # Basic cron validation - should have 5 parts separated by spaces
152
+ parts = cron_timer.strip().split()
153
+ if len(parts) != 5:
154
+ return False
155
+
156
+ # Each part should contain only digits, *, /, -, or ,
157
+ cron_pattern = re.compile(r'^[0-9*,/-]+$')
158
+ return all(cron_pattern.match(part) for part in parts)
159
+
160
+ def _show_invalid_cron_message(self, cron_timer: str):
161
+ """Show message for invalid cron timer."""
162
+ return f"""# ❌ Invalid Cron Timer Format
163
+
164
+ **Provided:** `{cron_timer}`
165
+
166
+ **Cron format should be:** `minute hour day month weekday`
167
+
168
+ ## Valid Examples:
169
+ - `0 2 * * *` - Daily at 2:00 AM
170
+ - `30 14 * * 1` - Every Monday at 2:30 PM
171
+ - `0 */6 * * *` - Every 6 hours
172
+ - `15 10 1 * *` - First day of every month at 10:15 AM
173
+ - `0 9 * * 1-5` - Weekdays at 9:00 AM
174
+
175
+ ## Format Rules:
176
+ - **Minute:** 0-59
177
+ - **Hour:** 0-23
178
+ - **Day:** 1-31
179
+ - **Month:** 1-12
180
+ - **Weekday:** 0-7 (0 and 7 are Sunday)
181
+ - Use **`*`** for "any value"
182
+ - Use **`,`** for multiple values
183
+ - Use **`-`** for ranges
184
+ - Use **`/`** for step values"""
185
+
186
+ def _parse_and_update_test_data(self, get_data: dict, schedule_name: str, cron_timer: str) -> dict:
187
+ """Parse GET response data and transform it into the required format for PUT request."""
188
+
189
+ # Extract environment and test type from test parameters
190
+ env_type = ""
191
+ test_type = ""
192
+ for param in get_data.get("test_parameters", []):
193
+ if param.get("name") == "env_type":
194
+ env_type = param.get("default", "")
195
+ elif param.get("name") == "test_type":
196
+ test_type = param.get("default", "")
197
+
198
+ # Construct common_params from GET data
199
+ common_params = {
200
+ "aggregation": get_data.get("aggregation", "max"),
201
+ "cc_env_vars": get_data.get("cc_env_vars", {}),
202
+ "entrypoint": get_data.get("entrypoint", ""),
203
+ "env_type": env_type,
204
+ "env_vars": get_data.get("env_vars", {}),
205
+ "location": get_data.get("location", ""),
206
+ "loops": get_data.get("loops", 1),
207
+ "name": get_data.get("name", ""),
208
+ "parallel_runners": get_data.get("parallel_runners", 1),
209
+ "runner": get_data.get("runner", ""),
210
+ "source": get_data.get("source", {}),
211
+ "test_type": test_type
212
+ }
213
+
214
+ # Extract only required integrations (reporters and system)
215
+ integrations = {
216
+ "reporters": get_data.get("integrations", {}).get("reporters", {}),
217
+ "system": get_data.get("integrations", {}).get("system", {})
218
+ }
219
+
220
+ # Process existing schedules and add the new one
221
+ schedules = []
222
+
223
+ # Keep existing schedules
224
+ for schedule in get_data.get("schedules", []):
225
+ existing_schedule = {
226
+ "active": schedule.get("active", False),
227
+ "cron": schedule.get("cron", ""),
228
+ "cron_radio": "custom",
229
+ "errors": {},
230
+ "id": schedule.get("id"),
231
+ "name": schedule.get("name", ""),
232
+ "project_id": schedule.get("project_id"),
233
+ "rpc_kwargs": schedule.get("rpc_kwargs"),
234
+ "test_id": schedule.get("test_id"),
235
+ "test_params": schedule.get("test_params", [])
236
+ }
237
+ schedules.append(existing_schedule)
238
+
239
+ # Add the new schedule
240
+ new_schedule = {
241
+ "active": True,
242
+ "cron": cron_timer,
243
+ "cron_radio": "custom",
244
+ "errors": {},
245
+ "id": None, # New schedule, no ID yet
246
+ "name": schedule_name,
247
+ "test_params": []
248
+ }
249
+ schedules.append(new_schedule)
250
+
251
+ # Assemble the final PUT request data
252
+ put_data = {
253
+ "common_params": common_params,
254
+ "integrations": integrations,
255
+ "run_test": False,
256
+ "schedules": schedules,
257
+ "test_parameters": [] # Empty as required in PUT request
258
+ }
259
+
260
+ return put_data
261
+
262
+ def _format_success_message(self, test_name: str, test_id: int, schedule_name: str, cron_timer: str) -> str:
263
+ """Format success message in markdown."""
264
+ return f"""# ✅ UI Test Schedule Updated Successfully!
265
+
266
+ ## Test Information:
267
+ - **Test Name:** `{test_name}`
268
+ - **Test ID:** `{test_id}`
269
+
270
+ ## New Schedule Added:
271
+ - **Schedule Name:** `{schedule_name}`
272
+ - **Cron Timer:** `{cron_timer}`
273
+ - **Status:** Active ✅
274
+
275
+ ## 🎯 What happens next:
276
+ The test will now run automatically according to the specified schedule. You can view and manage schedules in the Carrier platform UI.
277
+
278
+ **Schedule will execute:** Based on cron expression `{cron_timer}`"""
@@ -15,6 +15,13 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
15
15
 
16
16
  name = "memory"
17
17
 
18
+ def get_tools(tool):
19
+ return MemoryToolkit().get_toolkit(
20
+ namespace=tool['settings'].get('namespace', str(tool['id'])),
21
+ store=tool['settings'].get('store', None),
22
+ toolkit_name=tool.get('toolkit_name', '')
23
+ ).get_tools()
24
+
18
25
  class MemoryToolkit(BaseToolkit):
19
26
  tools: List[BaseTool] = []
20
27
 
@@ -1,4 +1,5 @@
1
1
  from typing import List, Literal, Optional, Type
2
+ import json
2
3
 
3
4
  import requests
4
5
  from langchain_core.tools import BaseToolkit, BaseTool
@@ -25,6 +26,8 @@ class PostmanAction(BaseAction):
25
26
  return v.replace(' ', '')
26
27
 
27
28
  def get_tools(tool):
29
+ # Parse environment_config if it's a string (from UI)
30
+ environment_config = tool['settings'].get('environment_config', {})
28
31
  toolkit = PostmanToolkit.get_toolkit(
29
32
  selected_tools=tool['settings'].get('selected_tools', []),
30
33
  api_key=tool['settings'].get('api_key', None),
@@ -32,6 +35,7 @@ def get_tools(tool):
32
35
  'base_url', 'https://api.getpostman.com'),
33
36
  collection_id=tool['settings'].get('collection_id', None),
34
37
  workspace_id=tool['settings'].get('workspace_id', None),
38
+ environment_config=environment_config,
35
39
  toolkit_name=tool.get('toolkit_name')
36
40
  )
37
41
  return toolkit.tools
@@ -57,6 +61,9 @@ class PostmanToolkit(BaseToolkit):
57
61
  'toolkit_name': True, 'max_toolkit_length': PostmanToolkit.toolkit_max_length})),
58
62
  workspace_id=(str, Field(description="Default workspace ID",
59
63
  default="", json_schema_extra={'configuration': True})),
64
+ environment_config=(dict, Field(
65
+ description="JSON configuration for request execution (auth headers, project IDs, base URLs, etc.)",
66
+ default={})),
60
67
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(
61
68
  default=[], json_schema_extra={'args_schemas': selected_tools})),
62
69
  __config__=ConfigDict(json_schema_extra={'metadata': {