alita-sdk 0.3.174__py3-none-any.whl → 0.3.176__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.
@@ -46,9 +46,18 @@ class AzureDevOpsReposToolkit(BaseToolkit):
46
46
  AzureDevOpsReposToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
47
47
  m = create_model(
48
48
  name,
49
- organization_url=(Optional[str], Field(default="", title="Organization URL", description="ADO organization url", json_schema_extra={'configuration': True})),
49
+ organization_url=(Optional[str], Field(default="", title="Organization URL",
50
+ description="ADO organization url",
51
+ json_schema_extra={
52
+ 'configuration': True,
53
+ "configuration_title": True
54
+ })),
50
55
  project=(Optional[str], Field(default="", title="Project", description="ADO project", json_schema_extra={'configuration': True})),
51
- repository_id=(Optional[str], Field(default="", title="Repository ID", description="ADO repository ID", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsReposToolkit.toolkit_max_length, 'configuration': True})),
56
+ repository_id=(Optional[str], Field(default="", title="Repository ID", description="ADO repository ID",
57
+ json_schema_extra={
58
+ 'toolkit_name': True,
59
+ 'max_toolkit_length': AzureDevOpsReposToolkit.toolkit_max_length,
60
+ 'configuration': True})),
52
61
  token=(Optional[SecretStr], Field(default="", title="Token", description="ADO token", json_schema_extra={'secret': True, 'configuration': True})),
53
62
  base_branch=(Optional[str], Field(default="", title="Base branch", description="ADO base branch (e.g., main)")),
54
63
  active_branch=(Optional[str], Field(default="", title="Active branch", description="ADO active branch (e.g., main)")),
@@ -24,7 +24,12 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
24
24
  m = create_model(
25
25
  name_alias,
26
26
  name=(str, Field(description="Toolkit name", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsPlansToolkit.toolkit_max_length})),
27
- organization_url=(str, Field(description="ADO organization url", json_schema_extra={'configuration': True})),
27
+ organization_url=(str, Field(default="", title="Organization URL",
28
+ description="ADO organization url",
29
+ json_schema_extra={
30
+ 'configuration': True,
31
+ "configuration_title": True
32
+ })),
28
33
  limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
29
34
  token=(SecretStr, Field(description="ADO token", json_schema_extra={'secret': True, 'configuration': True})),
30
35
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
@@ -25,7 +25,12 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
25
25
  'toolkit_name': True,
26
26
  'max_toolkit_length': AzureDevOpsWikiToolkit.toolkit_max_length})
27
27
  ),
28
- organization_url=(str, Field(description="ADO organization url", json_schema_extra={'configuration': True})),
28
+ organization_url=(str, Field(default="", title="Organization URL",
29
+ description="ADO organization url",
30
+ json_schema_extra={
31
+ 'configuration': True,
32
+ "configuration_title": True
33
+ })),
29
34
  project=(str, Field(description="ADO project", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsWikiToolkit.toolkit_max_length, 'configuration': True})),
30
35
  token=(SecretStr, Field(description="ADO token", json_schema_extra={'secret': True, 'configuration': True})),
31
36
  selected_tools=(List[Literal[tuple(selected_tools)]],
@@ -24,7 +24,12 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
24
24
  'toolkit_name': True,
25
25
  'max_toolkit_length': AzureDevOpsWorkItemsToolkit.toolkit_max_length})
26
26
  ),
27
- organization_url=(str, Field(description="ADO organization url", json_schema_extra={'configuration': True})),
27
+ organization_url=(str, Field(default="", title="Organization URL",
28
+ description="ADO organization url",
29
+ json_schema_extra={
30
+ 'configuration': True,
31
+ "configuration_title": True
32
+ })),
28
33
  project=(str, Field(description="ADO project", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsWorkItemsToolkit.toolkit_max_length, 'configuration': True})),
29
34
  token=(SecretStr, Field(description="ADO token", json_schema_extra={'secret': True, 'configuration': True})),
30
35
  limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
@@ -39,9 +39,18 @@ class AzureSearchToolkit(BaseToolkit):
39
39
  m = create_model(
40
40
  name,
41
41
  api_key=(SecretStr, Field(description="API key", json_schema_extra={'secret': True, 'configuration': True})),
42
- endpoint=(str, Field(description="Azure Search endpoint")),
42
+ endpoint=(str, Field(title="Azure Search endpoint",
43
+ description="Azure Search endpoint",
44
+ json_schema_extra={
45
+ 'configuration': True,
46
+ "configuration_title": True
47
+ })),
43
48
  index_name=(str, Field(description="Azure Search index name")),
44
- api_base=(Optional[str], Field(description="Azure OpenAI base URL", default=None, json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureSearchToolkit.toolkit_max_length, 'configuration': True})),
49
+ api_base=(Optional[str], Field(description="Azure OpenAI base URL", default=None,
50
+ json_schema_extra={
51
+ 'toolkit_name': True,
52
+ 'max_toolkit_length': AzureSearchToolkit.toolkit_max_length,
53
+ 'configuration': True})),
45
54
  api_version=(Optional[str], Field(description="API version", default=None)),
46
55
  openai_api_key=(Optional[str], Field(description="Azure OpenAI API Key", default=None, json_schema_extra={'secret': True})),
47
56
  model_name=(str, Field(description="Model name for Embeddings model", default=None)),
@@ -41,7 +41,7 @@ class AlitaBitbucketToolkit(BaseToolkit):
41
41
  AlitaBitbucketToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
42
42
  m = create_model(
43
43
  name,
44
- url=(str, Field(description="Bitbucket URL", json_schema_extra={'configuration': True})),
44
+ url=(str, Field(description="Bitbucket URL", json_schema_extra={'configuration': True, 'configuration_title': True})),
45
45
  project=(str, Field(description="Project/Workspace", json_schema_extra={'configuration': True})),
46
46
  repository=(str, Field(description="Repository", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AlitaBitbucketToolkit.toolkit_max_length, 'configuration': True})),
47
47
  branch=(str, Field(description="Main branch", default="main")),
@@ -6,6 +6,7 @@ from langchain_core.tools import BaseTool, ToolException
6
6
  from pydantic.fields import Field
7
7
  from pydantic import create_model, BaseModel
8
8
  from .api_wrapper import CarrierAPIWrapper
9
+ from .carrier_sdk import CarrierAPIError
9
10
 
10
11
  logger = logging.getLogger(__name__)
11
12
 
@@ -16,39 +17,26 @@ class CreateUITestTool(BaseTool):
16
17
  description: str = "Create a new UI test in the Carrier platform."
17
18
  args_schema: Type[BaseModel] = create_model(
18
19
  "CreateUITestInput",
19
- **{
20
- "name": (str, Field(default="", description="Test name")),
21
- "test_type": (str, Field(default="", description="Test type")),
22
- "env_type": (str, Field(default="", description="Environment type")),
23
- "entrypoint": (str, Field(default="", description="Entry point file (e.g., my_test.js)")),
24
- "runner": (str, Field(default="", description="Test runner type")),
25
- "repo": (str, Field(default="", description="Git repository URL")),
26
- "branch": (str, Field(default="", description="Git branch name")),
27
- "username": (str, Field(default="", description="Git username")),
28
- "password": (str, Field(default="", description="Git password")),
29
- "cpu_quota": (int, Field(default=2, description="CPU quota (cores)")),
30
- "memory_quota": (int, Field(default=5, description="Memory quota (GB)")),
31
- "custom_cmd": (str, Field(default="", description="Optional custom command")),
32
- "parallel_runners": (int, Field(default=1, description="Number of parallel runners")),
33
- "loops": (int, Field(default=1, description="Number of loops")),
34
- "aggregation": (str, Field(default="max", description="Aggregation method (max, min, avg)")),
35
- }
20
+ message=(str, Field(description="User request message for creating UI test")),
21
+ name=(str, Field(description="Test name (e.g., 'My UI Test')")),
22
+ test_type=(str, Field(description="Test type (e.g., 'performance')")),
23
+ env_type=(str, Field(description="Environment type (e.g., 'staging')")),
24
+ entrypoint=(str, Field(description="Entry point file (e.g., 'my_test.js')")),
25
+ runner=(str, Field(description="Test runner type. Available runners: Lighthouse-NPM_V12, Lighthouse-Nodejs, Lighthouse-NPM, Lighthouse-NPM_V11, Sitespeed (Browsertime), Sitespeed (New Entrypoint BETA), Sitespeed (New Version BETA), Sitespeed V36")),
26
+ repo=(str, Field(description="Git repository URL (e.g., 'https://github.com/user/repo.git')")),
27
+ branch=(str, Field(description="Git branch name (e.g., 'main')")),
28
+ username=(str, Field(description="Git username")),
29
+ password=(str, Field(description="Git password")),
30
+ cpu_quota=(int, Field(description="CPU quota in cores (e.g., 2)")),
31
+ memory_quota=(int, Field(description="Memory quota in GB (e.g., 5)")),
32
+ parallel_runners=(int, Field(description="Number of parallel runners (e.g., 1)")),
33
+ loops=(int, Field(description="Number of loops (e.g., 1)")),
34
+ **{"custom_cmd": (str, Field(default="", description="Optional custom command (e.g., '--login=\"qwerty\"')"))}
36
35
  )
37
36
 
38
37
  def _run(self, **kwargs):
39
38
  try:
40
- # Check if all required parameters are provided
41
- required_params = ["name", "test_type", "env_type", "entrypoint", "runner", "repo", "branch", "username", "password"]
42
- missing_params = []
43
-
44
- for param in required_params:
45
- if not kwargs.get(param) or kwargs.get(param).strip() == "":
46
- missing_params.append(param)
47
-
48
- if missing_params:
49
- return self._missing_parameters_response(missing_params)
50
-
51
- # Create the UI test
39
+ # Create the UI test with provided parameters
52
40
  return self._create_ui_test(kwargs)
53
41
 
54
42
  except Exception:
@@ -56,71 +44,34 @@ class CreateUITestTool(BaseTool):
56
44
  logger.error(f"Error creating UI test: {stacktrace}")
57
45
  raise ToolException(stacktrace)
58
46
 
59
- def _missing_parameters_response(self, missing_params=None):
60
- """Response when required parameters are missing."""
61
- available_runners = [
62
- "Lighthouse-NPM_V12",
63
- "Lighthouse-Nodejs",
64
- "Lighthouse-NPM",
65
- "Lighthouse-NPM_V11",
66
- "Sitespeed (Browsertime)",
67
- "Sitespeed (New Entrypoint BETA)",
68
- "Sitespeed (New Version BETA)",
69
- "Sitespeed V36"
70
- ]
71
-
72
- message = [
73
- "# 📝 Create UI Test - Required Parameters",
74
- "",
75
- "To create a new UI test, please provide the following parameters:",
76
- "",
77
- "## 🔴 Required Parameters:",
78
- "- **name**: Test name (e.g., 'My UI Test')",
79
- "- **test_type**: Test type (e.g., 'performance')",
80
- "- **env_type**: Environment type (e.g., 'staging')",
81
- "- **entrypoint**: Entry point file (e.g., 'my_test.js')",
82
- "- **runner**: Test runner (see available options below)",
83
- "- **repo**: Git repository URL (e.g., 'https://github.com/user/repo.git')",
84
- "- **branch**: Git branch name (e.g., 'main')",
85
- "- **username**: Git username",
86
- "- **password**: Git password",
87
- "",
88
- "## 🟡 Optional Parameters:",
89
- "- **cpu_quota**: CPU quota in cores (default: 2)",
90
- "- **memory_quota**: Memory quota in GB (default: 5)", "- **custom_cmd**: Optional custom command (e.g., '--login=\"qwerty\"')",
91
- "- **parallel_runners**: Number of parallel runners (default: 1)",
92
- "- **loops**: Number of loops (default: 1)",
93
- "",
94
- "## 🚀 Available Runners:",
95
- ]
96
-
97
- for runner in available_runners:
98
- message.append(f"- {runner}")
99
-
100
- message.extend([
101
- "",
102
- "## 💡 Example:",
103
- "```",
104
- "name: 'My Performance Test'",
105
- "test_type: 'performance'",
106
- "env_type: 'staging'",
107
- "entrypoint: 'lighthouse_test.js'",
108
- "runner: 'Lighthouse-NPM_V12'",
109
- "repo: 'https://github.com/mycompany/tests.git'", "branch: 'main'",
110
- "username: 'myusername'",
111
- "password: 'mypassword'",
112
- "```",
113
- "",
114
- "**Note:** Aggregation method is automatically set to 'max'."
115
- ])
116
-
117
- if missing_params:
118
- message.insert(2, f"❌ **Missing parameters:** {', '.join(missing_params)}")
119
- message.insert(3, "")
120
-
121
- return {
122
- "message": "\n".join(message)
123
- }
47
+ def _parse_validation_error(self, error_message: str) -> str:
48
+ """Parse validation error message and format it for user display."""
49
+ try:
50
+ # Try to extract JSON validation errors from the message
51
+ import re
52
+ json_match = re.search(r'\[.*\]', error_message)
53
+ if json_match:
54
+ json_str = json_match.group(0)
55
+ try:
56
+ validation_errors = json.loads(json_str)
57
+ if isinstance(validation_errors, list):
58
+ formatted_errors = []
59
+ for error in validation_errors:
60
+ if isinstance(error, dict):
61
+ field = error.get("loc", ["unknown"])[0] if error.get("loc") else "unknown"
62
+ message = error.get("msg", "Invalid value")
63
+ formatted_errors.append(f"- **{field}**: {message}")
64
+
65
+ if formatted_errors:
66
+ return "\n".join(formatted_errors)
67
+ except json.JSONDecodeError:
68
+ pass
69
+
70
+ # If we can't parse JSON, return the original message
71
+ return error_message
72
+
73
+ except Exception:
74
+ return error_message
124
75
 
125
76
  def _create_ui_test(self, params):
126
77
  """Create UI test using the provided parameters."""
@@ -139,25 +90,23 @@ class CreateUITestTool(BaseTool):
139
90
  "branch": params["branch"],
140
91
  "username": params["username"],
141
92
  "password": params["password"]
142
- },
143
- "env_vars": {
144
- "cpu_quota": params.get("cpu_quota", 2),
145
- "memory_quota": params.get("memory_quota", 5),
93
+ }, "env_vars": {
94
+ "cpu_quota": params["cpu_quota"],
95
+ "memory_quota": params["memory_quota"],
146
96
  "cloud_settings": {}
147
97
  },
148
- "parallel_runners": params.get("parallel_runners", 1),
98
+ "parallel_runners": params["parallel_runners"],
149
99
  "cc_env_vars": {},
150
100
  "location": "default",
151
- "loops": params.get("loops", 1),
152
- "aggregation": params.get("aggregation", "max")
101
+ "loops": params["loops"],
102
+ "aggregation": "max"
153
103
  },
154
104
  "test_parameters": [],
155
105
  "integrations": {},
156
106
  "schedules": [],
157
107
  "run_test": False
158
108
  }
159
-
160
- # Add custom_cmd if provided
109
+ # Add custom_cmd if provided
161
110
  if params.get("custom_cmd") and params["custom_cmd"].strip():
162
111
  post_body["common_params"]["env_vars"]["custom_cmd"] = params["custom_cmd"]
163
112
 
@@ -180,11 +129,11 @@ class CreateUITestTool(BaseTool):
180
129
  - **Entry Point:** `{params['entrypoint']}`
181
130
 
182
131
  ## Configuration:
183
- - **CPU Quota:** {params.get('cpu_quota', 2)} cores
184
- - **Memory Quota:** {params.get('memory_quota', 5)} GB
185
- - **Parallel Runners:** {params.get('parallel_runners', 1)}
186
- - **Loops:** {params.get('loops', 1)}
187
- - **Aggregation:** {params.get('aggregation', 'max')}
132
+ - **CPU Quota:** {params['cpu_quota']} cores
133
+ - **Memory Quota:** {params['memory_quota']} GB
134
+ - **Parallel Runners:** {params['parallel_runners']}
135
+ - **Loops:** {params['loops']}
136
+ - **Aggregation:** max
188
137
  {f"- **Custom Command:** `{params['custom_cmd']}`" if params.get('custom_cmd') else ""}
189
138
 
190
139
  ## 🎯 Next Steps:
@@ -194,6 +143,38 @@ class CreateUITestTool(BaseTool):
194
143
  else:
195
144
  return "❌ **Failed to create UI test. Please check your parameters and try again.**"
196
145
 
146
+ except CarrierAPIError as api_error:
147
+ # Handle API-specific errors with detailed validation messages
148
+ error_message = str(api_error)
149
+ logger.error(f"CarrierAPIError creating UI test: {error_message}")
150
+ # Try to extract validation details from the error message
151
+ if "400" in error_message:
152
+ parsed_errors = self._parse_validation_error(error_message)
153
+ return f"""# ❌ UI Test Creation Failed - Validation Error
154
+
155
+ ## 🚫 Invalid Input Parameters:
156
+ The Carrier platform rejected your request due to validation errors.
157
+
158
+ ## 📋 Validation Errors:
159
+ {parsed_errors}
160
+
161
+ ## 💡 Common Issues:
162
+ - **Test name**: Only letters, numbers, and "_" are allowed
163
+ - **Repository URL**: Must be a valid Git repository URL
164
+ - **Runner**: Must be one of the available runner types: Lighthouse-NPM_V12, Lighthouse-Nodejs, Lighthouse-NPM, Lighthouse-NPM_V11, Sitespeed (Browsertime), Sitespeed (New Entrypoint BETA), Sitespeed (New Version BETA), Sitespeed V36
165
+ - **Numeric values**: CPU quota, memory quota, parallel runners, and loops must be positive integers
166
+
167
+ ## 🔧 Please fix the validation errors above and try again."""
168
+ else:
169
+ return f"""# ❌ UI Test Creation Failed
170
+
171
+ ## 🚫 API Error:
172
+ ```
173
+ {error_message}
174
+ ```
175
+
176
+ ## 💡 Please check your parameters and try again."""
177
+
197
178
  except Exception as e:
198
179
  logger.error(f"Error creating UI test: {e}")
199
- raise ToolException(f"Failed to create UI test: {str(e)}")
180
+ raise ToolException(f"Failed to create UI test: {str(e)}")
@@ -16,30 +16,32 @@ class RunUITestTool(BaseTool):
16
16
  name: str = "run_ui_test"
17
17
  description: str = ("Run and execute UI tests from the Carrier platform. Use this tool when user wants to run, execute, or start a UI test. "
18
18
  "Provide either test ID or test name, or leave empty to see available tests. "
19
- "Optionally provide custom parameters like loops, cpu_quota, memory_quota, cloud_settings, or custom_cmd.")
19
+ "When no custom parameters are provided, the tool will show default configuration and ask for confirmation. "
20
+ "You can override parameters like cpu_quota, memory_quota, cloud_settings, custom_cmd, or loops. "
21
+ )
20
22
  args_schema: Type[BaseModel] = create_model(
21
23
  "RunUITestInput",
22
24
  test_id=(str, Field(default="", description="Test ID to execute")),
23
25
  test_name=(str, Field(default="", description="Test name to execute")),
24
- loops=(int, Field(default=None, description="Number of loops to run the test")),
25
26
  cpu_quota=(str, Field(default=None, description="CPU quota for the test runner")),
26
27
  memory_quota=(str, Field(default=None, description="Memory quota for the test runner")),
27
28
  cloud_settings=(str, Field(default=None, description="Cloud settings name for the test runner")),
28
29
  custom_cmd=(str, Field(default=None, description="Custom command to run with the test")),
30
+ loops=(str, Field(default=None, description="Number of loops to run the test")),
31
+ proceed_with_defaults=(bool, Field(default=False, description="Proceed with default configuration. True ONLY when user directly wants to run the test with default parameters." \
32
+ " If cpu_quota, memory_quota, cloud_settings, custom_cmd, or loops are provided, proceed_with_defaults must be False")),
29
33
  )
30
34
 
31
- def _run(self, test_id: str = "", test_name: str = "", loops: int = None,
35
+ def _run(self, test_id: str = "", test_name: str = "",
32
36
  cpu_quota: str = None, memory_quota: str = None,
33
- cloud_settings: str = None, custom_cmd: str = None):
37
+ cloud_settings: str = None, custom_cmd: str = None, loops: str = None,
38
+ proceed_with_defaults: bool = False):
34
39
  try:
35
40
  # Check if neither test_id nor test_name is provided
36
41
  if (not test_id or test_id.strip() == "") and (not test_name or test_name.strip() == ""):
37
42
  return self._missing_input_response()
38
-
39
- # Check if user wants to see the list of tests (can be in test_name field)
40
- if test_name.lower() in ["show me the list of ui tests", "list ui tests", "show ui tests"]:
41
- return self._show_ui_tests_list()
42
- # Get UI tests list only when we need to search for and run a test
43
+
44
+ # Get UI tests list only when we need to search for and run a test
43
45
  ui_tests = self.api_wrapper.get_ui_tests_list()
44
46
 
45
47
  # Find the test by ID or name
@@ -91,36 +93,81 @@ class RunUITestTool(BaseTool):
91
93
  for test in ui_tests:
92
94
  available_tests.append(f"ID: {test.get('id')}, Name: {test.get('name')}")
93
95
 
96
+ available_locations = self._get_available_locations()
97
+
94
98
  search_criteria = []
95
99
  if test_id:
96
100
  search_criteria.append(f"ID: {test_id}")
97
101
  if test_name:
98
102
  search_criteria.append(f"Name: {test_name}")
99
103
 
100
- return f"Test not found for {' or '.join(search_criteria)}. Available UI tests:\n" + "\n".join(available_tests)
104
+ error_message = []
105
+ error_message.append(f"Test not found for {' or '.join(search_criteria)}.")
106
+ error_message.append("")
107
+ error_message.append("Available UI tests:")
108
+ error_message.extend(available_tests)
109
+ error_message.append("")
110
+ error_message.append("Available runners/locations:")
111
+ error_message.extend(available_locations)
112
+
113
+ return "\n".join(error_message)
101
114
 
102
- # Check if custom parameters are provided
103
- has_custom_params = any([loops is not None, cpu_quota is not None, memory_quota is not None,
104
- cloud_settings is not None, custom_cmd is not None])
115
+ # Check if custom parameters are provided (including cloud_settings)
116
+ has_custom_params = any([cpu_quota is not None, memory_quota is not None,
117
+ cloud_settings is not None, custom_cmd is not None, loops is not None])
105
118
 
106
- # If no custom parameters provided, show info message with default values and available options
107
- if not has_custom_params:
108
- return self._show_test_parameter_info(test_data, ui_test_id)
109
-
110
- # Get detailed test configuration for the POST request
111
- test_details = self._get_ui_test_details(ui_test_id)
119
+ if has_custom_params:
120
+ proceed_with_defaults = False # If any custom params are provided, do not proceed with defaults
121
+
122
+ # Get detailed test configuration from API (not from UI tests list)
123
+ test_details = self.api_wrapper.get_ui_test_details(str(ui_test_id))
112
124
 
113
125
  if not test_details:
114
126
  return f"Could not retrieve test details for test ID {ui_test_id}."
115
127
 
116
- # Prepare POST request data with custom parameters
117
- post_data = self._prepare_post_data_with_custom_params(test_details, loops, cpu_quota, memory_quota, cloud_settings, custom_cmd)
128
+ # Validate cloud_settings location if provided
129
+ if cloud_settings and not self._validate_cloud_settings_location(cloud_settings):
130
+ available_locations = self._get_available_locations()
131
+ error_message = []
132
+ error_message.append(f"❌ Invalid location/cloud_settings: '{cloud_settings}'")
133
+ error_message.append("")
134
+ error_message.append("Available runners/locations:")
135
+ error_message.extend(available_locations)
136
+ error_message.append("")
137
+ error_message.append("Please choose a valid location name from the list above.")
138
+ return "\n".join(error_message)
139
+
140
+ # If no custom parameters provided, check if user wants to proceed with defaults
141
+ if not has_custom_params:
142
+ if not proceed_with_defaults:
143
+ # Show default configuration and ask user if they want to change anything
144
+ default_message = self._show_default_configuration_message(test_details)
145
+ if isinstance(default_message, dict) and "message" in default_message:
146
+ message_str = "\n".join(default_message["message"])
147
+ else:
148
+ message_str = str(default_message)
149
+ return message_str + "\n\nTo proceed with default configuration, type `Run test with default configuration` or specify any parameters you want to override."
150
+ else:
151
+ # User confirmed to proceed with defaults
152
+ post_data = self._prepare_post_data_default(test_details)
153
+ else:
154
+ # Prepare POST request data with custom parameters or cloud settings
155
+ post_data = self._prepare_post_data_with_overrides(test_details, cpu_quota, memory_quota, cloud_settings, custom_cmd, loops)
118
156
 
119
157
  # Execute the UI test
120
158
  result_id = self.api_wrapper.run_ui_test(str(ui_test_id), post_data)
121
159
 
122
- return f"UI test started successfully. Result ID: {result_id}. " \
123
- f"Link to report: {self.api_wrapper.url.rstrip('/')}/-/performance/ui/results?result_id={result_id}"
160
+ # Show location info in success message
161
+ location_used = "default"
162
+ if cloud_settings:
163
+ location_used = cloud_settings
164
+ elif post_data.get("common_params", {}).get("location"):
165
+ location_used = post_data["common_params"]["location"]
166
+
167
+ return f"✅ UI test started successfully!\n" \
168
+ f"Result ID: {result_id}\n" \
169
+ f"Location used: {location_used}\n" \
170
+ f"Link to report: {self.api_wrapper._client.credentials.url.rstrip('/')}/-/performance/ui/results?result_id={result_id}"
124
171
 
125
172
  except Exception:
126
173
  stacktrace = traceback.format_exc()
@@ -146,122 +193,95 @@ class RunUITestTool(BaseTool):
146
193
  logger.error(f"Error fetching UI tests list: {stacktrace}")
147
194
  raise ToolException(stacktrace)
148
195
 
149
- def _get_ui_test_details(self, test_id: int):
150
- """Get detailed test configuration from the UI tests list."""
196
+ def _prepare_post_data_default(self, test_details):
197
+ """Prepare POST request data with default parameters."""
151
198
  try:
152
- ui_tests = self.api_wrapper.get_ui_tests_list()
199
+ # Extract values from the test details
200
+ test_parameters = test_details.get("test_parameters", [])
201
+ env_vars = test_details.get("env_vars", {})
202
+ integrations = test_details.get("integrations", {})
203
+ location = test_details.get("location", "")
204
+ parallel_runners = test_details.get("parallel_runners", 1)
205
+ aggregation = test_details.get("aggregation", "max")
206
+ loops = test_details.get("loops", 1)
153
207
 
154
- for test in ui_tests:
155
- if test.get("id") == test_id:
156
- return test
208
+ # Find specific test parameters by name
209
+ def find_param_by_name(params, name):
210
+ for param in params:
211
+ if param.get("name") == name:
212
+ return param
213
+ return {}
157
214
 
158
- return None
215
+ # Extract S3 integration
216
+ s3_integration = integrations.get("system", {}).get("s3_integration", {})
159
217
 
160
- except Exception:
161
- stacktrace = traceback.format_exc()
162
- logger.error(f"Error getting UI test details: {stacktrace}")
163
- return None
164
-
165
- def _show_test_parameter_info(self, test_data, test_id):
166
- """Show information about test parameters that can be changed."""
167
- try:
168
- # Get current default values from test data
169
- env_vars = test_data.get("env_vars", {})
170
-
171
- info_message = []
172
- info_message.append(f"Test '{test_data.get('name')}' (ID: {test_id}) found!")
173
- info_message.append("\nCurrent default parameters:")
174
- info_message.append(f"- loops: 1 (default override)")
175
- info_message.append(f"- cpu_quota: {env_vars.get('cpu_quota', 'Not set')}")
176
- info_message.append(f"- memory_quota: {env_vars.get('memory_quota', 'Not set')}")
177
- info_message.append(f"- cloud_settings: {env_vars.get('cloud_settings', 'Not set')}")
178
- info_message.append(f"- custom_cmd: {env_vars.get('custom_cmd', 'Not set')}")
179
- # Always try to get and display available cloud settings - this is critical information
180
- info_message.append("\n" + "="*60)
181
- info_message.append("🏃 AVAILABLE RUNNERS - CHOOSE ONE FOR cloud_settings:")
182
- info_message.append("="*60)
183
-
184
- try:
185
- locations_data = self.api_wrapper.get_locations()
186
- if not locations_data:
187
- info_message.append("⚠️ Could not fetch locations data - API returned empty response")
188
- else:
189
- cloud_regions = locations_data.get("cloud_regions", [])
190
- public_regions = locations_data.get("public_regions", [])
191
- project_regions = locations_data.get("project_regions", [])
192
- # Add public regions information (these are the most commonly used)
193
- info_message.append("\n🌐 PUBLIC REGIONS (use these names):")
194
- if public_regions:
195
- for region in public_regions:
196
- info_message.append(f" ✅ '{region}'")
197
- else:
198
- info_message.append(" ❌ No public regions available")
199
-
200
- # Add project regions information
201
- if project_regions:
202
- info_message.append("\n🏢 PROJECT REGIONS (use these names):")
203
- for region in project_regions:
204
- info_message.append(f" ✅ '{region}'")
205
-
206
- # Add cloud regions information
207
- if cloud_regions:
208
- info_message.append("\n☁️ CLOUD REGIONS (advanced - use full names):")
209
- for region in cloud_regions:
210
- region_name = region.get("name", "Unknown")
211
- info_message.append(f" ✅ '{region_name}'")
212
-
213
- except Exception as e:
214
- logger.error(f"Error fetching locations: {e}")
215
- info_message.append("❌ ERROR: Could not fetch available runners!")
216
- info_message.append(f" Reason: {str(e)}")
217
- info_message.append(" Please check your API connection and try again.")
218
-
219
- info_message.append("="*60)
220
- info_message.append("\n📋 HOW TO USE:")
221
- info_message.append("To run this test with custom parameters, specify the values you want to change.")
222
- info_message.append("\n💡 EXAMPLES:")
223
- info_message.append(" • Use default runner: cloud_settings='default'")
224
- info_message.append(" • Change loops: loops=5")
225
- info_message.append(" • Change resources: cpu_quota='2', memory_quota='4Gi'")
226
- info_message.append(" • Full example: loops=3, cloud_settings='default', cpu_quota='2'")
227
- info_message.append("\n📝 RUNNER TYPES:")
228
- info_message.append(" • Public regions: Use empty cloud_settings {}, location = runner name")
229
- info_message.append(" • Project regions: Use empty cloud_settings {}, location = runner name")
230
- info_message.append(" • Cloud regions: Use full cloud configuration object")
231
-
232
- return "\n".join(info_message)
218
+ # Get actual cloud_settings from test details (could be empty {} or have actual settings)
219
+ actual_cloud_settings = env_vars.get("cloud_settings", {})
220
+
221
+ # Prepare the POST request body with default parameters
222
+ post_data = {
223
+ "common_params": {
224
+ "name": find_param_by_name(test_parameters, "test_name"),
225
+ "test_type": find_param_by_name(test_parameters, "test_type"),
226
+ "env_type": find_param_by_name(test_parameters, "env_type"),
227
+ "env_vars": {
228
+ "cpu_quota": env_vars.get("cpu_quota"),
229
+ "memory_quota": env_vars.get("memory_quota"),
230
+ "cloud_settings": actual_cloud_settings, # Use actual cloud settings from test details
231
+ "ENV": env_vars.get("ENV", "prod"),
232
+ "custom_cmd": env_vars.get("custom_cmd", "")
233
+ },
234
+ "parallel_runners": parallel_runners,
235
+ "location": location
236
+ },
237
+ "test_parameters": test_parameters,
238
+ "integrations": {
239
+ "system": {
240
+ "s3_integration": {
241
+ "integration_id": s3_integration.get("integration_id"),
242
+ "is_local": s3_integration.get("is_local")
243
+ }
244
+ }
245
+ },
246
+ "loops": loops,
247
+ "aggregation": aggregation
248
+ }
249
+
250
+ return post_data
233
251
 
234
252
  except Exception:
235
253
  stacktrace = traceback.format_exc()
236
- logger.error(f"Error showing test parameter info: {stacktrace}")
254
+ logger.error(f"Error preparing default POST data: {stacktrace}")
237
255
  raise ToolException(stacktrace)
238
-
239
- def _prepare_post_data_with_custom_params(self, test_data, loops=None, cpu_quota=None,
240
- memory_quota=None, cloud_settings=None, custom_cmd=None):
241
- """Prepare POST request data with custom parameters."""
256
+
257
+ def _prepare_post_data_with_overrides(self, test_details, cpu_quota=None,
258
+ memory_quota=None, cloud_settings=None, custom_cmd=None, loops=None):
259
+ """Prepare POST request data with overrides or cloud settings."""
242
260
  try:
243
- # Extract values from the test data
244
- test_parameters = test_data.get("test_parameters", [])
245
- env_vars = test_data.get("env_vars", {})
246
- integrations = test_data.get("integrations", {})
247
- location = test_data.get("location", "")
248
- parallel_runners = test_data.get("parallel_runners", 1)
249
- aggregation = test_data.get("aggregation", "max")
261
+ # Extract values from the test details
262
+ test_parameters = test_details.get("test_parameters", [])
263
+ env_vars = test_details.get("env_vars", {})
264
+ integrations = test_details.get("integrations", {})
265
+ location = test_details.get("location", "")
266
+ parallel_runners = test_details.get("parallel_runners", 1)
267
+ aggregation = test_details.get("aggregation", "max")
268
+ default_loops = test_details.get("loops", 1)
269
+ # Use provided loops parameter or default
270
+ final_loops = loops if loops is not None else default_loops
250
271
 
251
- # Extract reporter email for integrations
252
- reporter_email = integrations.get("reporters", {}).get("reporter_email", {})
253
-
254
- # Extract S3 integration
255
- s3_integration = integrations.get("system", {}).get("s3_integration", {})
256
- # Find specific test parameters by name
272
+ # Find specific test parameters by name
257
273
  def find_param_by_name(params, name):
258
274
  for param in params:
259
275
  if param.get("name") == name:
260
276
  return param
261
- return {}
262
- # Handle cloud_settings parameter and location
263
- final_cloud_settings = env_vars.get("cloud_settings")
264
- final_location = location # Use original location as default
277
+ return {}
278
+
279
+ # Extract S3 integration
280
+ s3_integration = integrations.get("system", {}).get("s3_integration", {})
281
+
282
+ # Handle cloud_settings parameter and determine the scenario
283
+ final_cloud_settings = {}
284
+ final_location = location
265
285
 
266
286
  if cloud_settings:
267
287
  try:
@@ -270,65 +290,59 @@ class RunUITestTool(BaseTool):
270
290
  public_regions = locations_data.get("public_regions", [])
271
291
  project_regions = locations_data.get("project_regions", [])
272
292
 
273
- # Check if it's a public region first
274
- if cloud_settings in public_regions:
275
- # For public regions, use empty cloud_settings and set location to the runner name
276
- final_cloud_settings = {}
277
- final_location = cloud_settings
278
- # Check if it's a project region
279
- elif cloud_settings in project_regions:
280
- # For project regions, use empty cloud_settings and set location to the runner name
293
+ # Check if it's a public or project region (scenario 1)
294
+ if cloud_settings in public_regions or cloud_settings in project_regions:
295
+ # For public/project regions, use empty cloud_settings and set location
281
296
  final_cloud_settings = {}
282
297
  final_location = cloud_settings
283
298
  else:
284
- # Try to find exact match in cloud regions
285
- found_match = False
299
+ # Check if it's a cloud region (scenario 2)
300
+ found_cloud_region = None
286
301
  for region in cloud_regions:
287
302
  if region.get("name", "").lower() == cloud_settings.lower():
288
- # Get the complete cloud_settings object and add the missing fields
289
- region_cloud_settings = region.get("cloud_settings", {})
290
- # Add the additional fields that are expected in the POST request
291
- region_cloud_settings.update({
292
- "instance_type": "on-demand",
293
- "ec2_instance_type": "t2.xlarge",
294
- "cpu_cores_limit": int(cpu_quota) if cpu_quota else env_vars.get("cpu_quota", 1),
295
- "memory_limit": int(memory_quota) if memory_quota else env_vars.get("memory_quota", 8),
296
- "concurrency": 1
297
- })
298
- final_cloud_settings = region_cloud_settings
299
- final_location = location # Keep original location for cloud regions
300
- found_match = True
303
+ found_cloud_region = region
301
304
  break
302
305
 
303
- # If no exact match in cloud regions, try partial match
304
- if not found_match:
306
+ # If not exact match, try partial match
307
+ if not found_cloud_region:
305
308
  for region in cloud_regions:
306
309
  if cloud_settings.lower() in region.get("name", "").lower():
307
- region_cloud_settings = region.get("cloud_settings", {})
308
- region_cloud_settings.update({
309
- "instance_type": "on-demand",
310
- "ec2_instance_type": "t2.xlarge",
311
- "cpu_cores_limit": int(cpu_quota) if cpu_quota else env_vars.get("cpu_quota", 1),
312
- "memory_limit": int(memory_quota) if memory_quota else env_vars.get("memory_quota", 8),
313
- "concurrency": 1
314
- })
315
- final_cloud_settings = region_cloud_settings
316
- final_location = location # Keep original location for cloud regions
317
- found_match = True
310
+ found_cloud_region = region
318
311
  break
319
312
 
320
- # If still no match found, treat as public region fallback
321
- if not found_match:
313
+ if found_cloud_region:
314
+ # Extract cloud settings details for scenario 2
315
+ # cloud_name = found_cloud_region.get("name", "")
316
+ cloud_config = found_cloud_region.get("cloud_settings", {})
317
+ final_cloud_settings = {
318
+ "integration_name": cloud_config.get("integration_name"),
319
+ "id": cloud_config.get("id"),
320
+ "project_id": cloud_config.get("project_id"),
321
+ "aws_access_key": cloud_config.get("aws_access_key"),
322
+ "aws_secret_access_key": cloud_config.get("aws_secret_access_key", {}),
323
+ "region_name": cloud_config.get("region_name"),
324
+ "security_groups": cloud_config.get("security_groups"),
325
+ "image_id": cloud_config.get("image_id"),
326
+ "key_name": cloud_config.get("key_name", ""),
327
+ "instance_type": "spot",
328
+ "ec2_instance_type": "t2.xlarge"
329
+ }
330
+ final_location = found_cloud_region.get("name")
331
+ else:
332
+ # If no match found, treat as public region fallback
322
333
  final_cloud_settings = {}
323
334
  final_location = cloud_settings
324
335
 
325
336
  except Exception as e:
326
337
  logger.error(f"Error processing cloud_settings: {e}")
327
- # Use the provided value as-is if we can't process it
328
- final_cloud_settings = cloud_settings
329
- final_location = location
330
-
331
- # Prepare the POST request body with custom parameters
338
+ # Use empty cloud_settings as fallback
339
+ final_cloud_settings = {}
340
+ final_location = cloud_settings if cloud_settings else location
341
+ else:
342
+ # If no cloud_settings provided, use default from test details
343
+ final_cloud_settings = env_vars.get("cloud_settings", {})
344
+
345
+ # Prepare the POST request body with overrides
332
346
  post_data = {
333
347
  "common_params": {
334
348
  "name": find_param_by_name(test_parameters, "test_name"),
@@ -338,21 +352,14 @@ class RunUITestTool(BaseTool):
338
352
  "cpu_quota": cpu_quota if cpu_quota is not None else env_vars.get("cpu_quota"),
339
353
  "memory_quota": memory_quota if memory_quota is not None else env_vars.get("memory_quota"),
340
354
  "cloud_settings": final_cloud_settings,
341
- "ENV": "prod", # Override as per reference code
355
+ "ENV": env_vars.get("ENV", "prod"),
342
356
  "custom_cmd": custom_cmd if custom_cmd is not None else env_vars.get("custom_cmd", "")
343
- }, "parallel_runners": parallel_runners,
357
+ },
358
+ "parallel_runners": parallel_runners,
344
359
  "location": final_location
345
360
  },
346
361
  "test_parameters": test_parameters,
347
362
  "integrations": {
348
- "reporters": {
349
- "reporter_email": {
350
- "id": reporter_email.get("id"),
351
- "is_local": reporter_email.get("is_local"),
352
- "project_id": reporter_email.get("project_id"),
353
- "recipients": reporter_email.get("recipients")
354
- }
355
- },
356
363
  "system": {
357
364
  "s3_integration": {
358
365
  "integration_id": s3_integration.get("integration_id"),
@@ -360,7 +367,7 @@ class RunUITestTool(BaseTool):
360
367
  }
361
368
  }
362
369
  },
363
- "loops": loops if loops is not None else 1, # Use custom loops or default to 1
370
+ "loops": final_loops,
364
371
  "aggregation": aggregation
365
372
  }
366
373
 
@@ -368,20 +375,29 @@ class RunUITestTool(BaseTool):
368
375
 
369
376
  except Exception:
370
377
  stacktrace = traceback.format_exc()
371
- logger.error(f"Error preparing POST data with custom parameters: {stacktrace}")
378
+ logger.error(f"Error preparing POST data with overrides: {stacktrace}")
372
379
  raise ToolException(stacktrace)
373
380
 
374
381
  def _missing_input_response(self):
375
382
  """Response when required input is missing."""
376
383
  try:
377
384
  available_tests = self._show_ui_tests_list()
385
+ available_locations = self._get_available_locations()
386
+
387
+ location_info = "Available runners/locations for cloud_settings:\n" + "\n".join(available_locations)
388
+
378
389
  return {
379
- "message": "Please provide test ID or test name of your UI test.",
390
+ "message": {
391
+ "text": (
392
+ "Please provide test ID or test name of your UI test.\n\n"
393
+ "Available UI tests:\n" + available_tests + "\n\n"
394
+ "Available runners/locations for cloud_settings:\n" + location_info
395
+ ),
396
+ },
380
397
  "parameters": {
381
398
  "test_id": None,
382
399
  "test_name": None,
383
- },
384
- "available_tests": available_tests
400
+ }
385
401
  }
386
402
  except Exception:
387
403
  return {
@@ -390,5 +406,116 @@ class RunUITestTool(BaseTool):
390
406
  "test_id": None,
391
407
  "test_name": None,
392
408
  },
393
- "available_tests": "Error fetching available tests."
409
+ "available_tests": "Error fetching available tests.",
410
+ "available_locations": "Error fetching available locations."
394
411
  }
412
+
413
+ def _show_default_configuration_message(self, test_details):
414
+ """Show information about default configuration and available override parameters."""
415
+ try:
416
+ env_vars = test_details.get("env_vars", {})
417
+ test_name = test_details.get("name", "Unknown")
418
+ loops = test_details.get("loops", 1)
419
+
420
+ # Get available locations
421
+ available_locations = self._get_available_locations()
422
+
423
+ message = []
424
+ message.append("Current default parameters:")
425
+ message.append(f" • CPU Quota: {env_vars.get('cpu_quota', 'Not set')}")
426
+ message.append(f" • Memory Quota: {env_vars.get('memory_quota', 'Not set')}")
427
+ message.append(f" • Custom Command: {env_vars.get('custom_cmd', 'Not set')}")
428
+ message.append(f" • Loops: {loops}")
429
+ message.append(f" • Cloud Settings: Default location")
430
+ message.append("")
431
+ message.append("Available parameters to override:")
432
+ message.append(" • cpu_quota - Set CPU quota for the test runner")
433
+ message.append(" • memory_quota - Set memory quota for the test runner")
434
+ message.append(" • custom_cmd - Set custom command to run with the test")
435
+ message.append(" • loops - Set number of loops to run the test")
436
+ message.append(" • cloud_settings - Set cloud settings name for the test runner")
437
+ message.append("")
438
+ message.append("Available runners/locations:")
439
+ message.extend(available_locations)
440
+
441
+ return {"message": message}
442
+
443
+ except Exception:
444
+ stacktrace = traceback.format_exc()
445
+ logger.error(f"Error showing default configuration: {stacktrace}")
446
+ return "Default configuration available for this test."
447
+
448
+ def _get_available_locations(self):
449
+ """Get formatted list of available locations for user selection."""
450
+ try:
451
+ locations_data = self.api_wrapper.get_locations()
452
+ location_list = []
453
+
454
+ # Add public regions
455
+ public_regions = locations_data.get("public_regions", [])
456
+ if public_regions:
457
+ location_list.append(" Public Regions:")
458
+ for region in public_regions:
459
+ location_list.append(f" - {region}")
460
+
461
+ # Add project regions
462
+ project_regions = locations_data.get("project_regions", [])
463
+ if project_regions:
464
+ location_list.append(" Project Regions:")
465
+ for region in project_regions:
466
+ location_list.append(f" - {region}")
467
+
468
+ # Add cloud regions
469
+ cloud_regions = locations_data.get("cloud_regions", [])
470
+ if cloud_regions:
471
+ location_list.append(" Cloud Regions:")
472
+ for region in cloud_regions:
473
+ region_name = region.get("name", "Unknown")
474
+ cloud_config = region.get("cloud_settings", {})
475
+ aws_region = cloud_config.get("region_name", "")
476
+ location_list.append(f" - {region_name} ({aws_region})")
477
+
478
+ if not location_list:
479
+ location_list.append(" No locations available")
480
+
481
+ return location_list
482
+
483
+ except Exception:
484
+ stacktrace = traceback.format_exc()
485
+ logger.error(f"Error getting available locations: {stacktrace}")
486
+ return [" Error loading locations"]
487
+
488
+ def _validate_cloud_settings_location(self, cloud_settings):
489
+ """Validate if the provided cloud_settings location exists and is available."""
490
+ try:
491
+ if not cloud_settings:
492
+ return True # No cloud_settings provided, use default
493
+
494
+ locations_data = self.api_wrapper.get_locations()
495
+
496
+ # Check in public regions
497
+ public_regions = locations_data.get("public_regions", [])
498
+ if cloud_settings in public_regions:
499
+ return True
500
+
501
+ # Check in project regions
502
+ project_regions = locations_data.get("project_regions", [])
503
+ if cloud_settings in project_regions:
504
+ return True
505
+
506
+ # Check in cloud regions (by name)
507
+ cloud_regions = locations_data.get("cloud_regions", [])
508
+ for region in cloud_regions:
509
+ region_name = region.get("name", "")
510
+ if region_name.lower() == cloud_settings.lower():
511
+ return True
512
+ # Also check partial match
513
+ if cloud_settings.lower() in region_name.lower():
514
+ return True
515
+
516
+ return False
517
+
518
+ except Exception:
519
+ stacktrace = traceback.format_exc()
520
+ logger.error(f"Error validating cloud settings location: {stacktrace}")
521
+ return True # Allow execution if validation fails
@@ -51,13 +51,14 @@ class ConfluenceToolkit(BaseToolkit):
51
51
  headers = {'Accept': 'application/json'}
52
52
  auth = None
53
53
  if self.token:
54
- headers['Authorization'] = f'Bearer {self.token.get_secret_value()}'
54
+ headers['Authorization'] = f'Bearer {self.token}'
55
55
  elif self.username and self.api_key:
56
- auth = (self.username, self.api_key.get_secret_value())
56
+ auth = (self.username, self.api_key)
57
57
  else:
58
58
  raise ValueError('Confluence connection requires either token or username+api_key')
59
59
  response = requests.get(url, headers=headers, auth=auth, timeout=5, verify=getattr(self, 'verify_ssl', True))
60
60
  return response
61
+
61
62
  model = create_model(
62
63
  name,
63
64
  base_url=(str, Field(description="Confluence URL", json_schema_extra={'configuration': True, 'configuration_title': True})),
@@ -1507,6 +1507,7 @@ class ConfluenceAPIWrapper(BaseVectorStoreToolApiWrapper):
1507
1507
  'application/doc', 'application/docx',
1508
1508
  'application/xls', 'application/xlsx',
1509
1509
  'application/svg', 'application/html',
1510
+ 'application/octet-stream'
1510
1511
  ]
1511
1512
  or file_ext in [
1512
1513
  'xml', 'json', 'md', 'markdown', 'txt',
@@ -41,9 +41,9 @@ class JiraToolkit(BaseToolkit):
41
41
  headers = {'Accept': 'application/json'}
42
42
  auth = None
43
43
  if self.token:
44
- headers['Authorization'] = f'Bearer {self.token.get_secret_value()}'
44
+ headers['Authorization'] = f'Bearer {self.token}'
45
45
  elif self.username and self.api_key:
46
- auth = (self.username, self.api_key.get_secret_value())
46
+ auth = (self.username, self.api_key)
47
47
  else:
48
48
  raise ValueError('Jira connection requires either token or username+api_key')
49
49
  response = requests.get(url, headers=headers, auth=auth, timeout=5, verify=getattr(self, 'verify_ssl', True))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.174
3
+ Version: 0.3.176
4
4
  Summary: SDK for building langchain agents using resources from Alita
5
5
  Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedjik@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -123,6 +123,7 @@ Requires-Dist: pysnc==1.1.10; extra == "tools"
123
123
  Requires-Dist: shortuuid==1.0.13; extra == "tools"
124
124
  Requires-Dist: yarl==1.17.1; extra == "tools"
125
125
  Requires-Dist: langmem==0.0.27; extra == "tools"
126
+ Requires-Dist: textract-py3==2.1.1; extra == "tools"
126
127
  Provides-Extra: community
127
128
  Requires-Dist: retry-extended==0.2.3; extra == "community"
128
129
  Requires-Dist: browser-use==0.1.43; extra == "community"
@@ -133,22 +133,22 @@ alita_sdk/tools/__init__.py,sha256=48DhEi14KkaYhNb-KvXuM9XJ4WGC-v9sRcWfN7GFWd4,9
133
133
  alita_sdk/tools/elitea_base.py,sha256=NQaIxPX6DVIerHCb18jwUR6maZxxk73NZaTsFHkBQWE,21119
134
134
  alita_sdk/tools/ado/__init__.py,sha256=mD6GHcYMTtffPJkJvFPe2rzvye_IRmXmWfI7xYuZhO4,912
135
135
  alita_sdk/tools/ado/utils.py,sha256=PTCludvaQmPLakF2EbCGy66Mro4-rjDtavVP-xcB2Wc,1252
136
- alita_sdk/tools/ado/repos/__init__.py,sha256=-sMK94fLgtIAGoP26wXs466kF6AiZhdLDdr_--o4ELU,5818
136
+ alita_sdk/tools/ado/repos/__init__.py,sha256=cS7yON7bwQxAu7CI7-SeSLCB4c83awjbLj3ZB1mWN2I,6317
137
137
  alita_sdk/tools/ado/repos/repos_wrapper.py,sha256=_OWKAls7VFfFtEPTwqj_DxE1MSvpC0ivxdTIULEz3Tk,48206
138
- alita_sdk/tools/ado/test_plan/__init__.py,sha256=PmVBwOQExgDmiywQEL9D29hbX3fC1pJd6bROnyBOu_k,3916
138
+ alita_sdk/tools/ado/test_plan/__init__.py,sha256=1DMvP4GjgBhrLPSQl6KNbNkInFqVFsDEeLBX99rEhfU,4248
139
139
  alita_sdk/tools/ado/test_plan/test_plan_wrapper.py,sha256=oIvVhLUMP5ZGctoAtK6sU0y6Si9gNv9-mbLqcWtw3gY,12525
140
- alita_sdk/tools/ado/wiki/__init__.py,sha256=6WRwKBznxXP5vSDpPT6i5njPgPsrUUOJ8U1qeH3wtGk,4265
140
+ alita_sdk/tools/ado/wiki/__init__.py,sha256=eiV2uJ7s0hnok68v5eRr48bBSArraHr0bv_3AsxnZiQ,4597
141
141
  alita_sdk/tools/ado/wiki/ado_wrapper.py,sha256=l4bc2QoKSUXg9UqNcx0ylv7YL9JPPQd35Ti5MXyEgC4,12690
142
- alita_sdk/tools/ado/work_item/__init__.py,sha256=kLIFw7w_3ygqqPjzxWJ8cC9cbOxNl_yfxjhI7RGs1Ys,4368
142
+ alita_sdk/tools/ado/work_item/__init__.py,sha256=WxlxoAZKDMVqb2t7ijrCE7nCBAx5kKuCRes9t8RJv4M,4700
143
143
  alita_sdk/tools/ado/work_item/ado_wrapper.py,sha256=t0D9xubU0yy_JmRJ_zEtRCxwFLyanT1StbIrtHGaqpw,26108
144
144
  alita_sdk/tools/advanced_jira_mining/__init__.py,sha256=pUTzECqGvYaR5qWY3JPUhrImrZgc7pCXuqSe5eWIE80,4604
145
145
  alita_sdk/tools/advanced_jira_mining/data_mining_wrapper.py,sha256=nZPtuwVWp8VeHw1B8q9kdwf-6ZvHnlXTOGdcIMDkKpw,44211
146
146
  alita_sdk/tools/azure_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
- alita_sdk/tools/azure_ai/search/__init__.py,sha256=CYqGc6jPkXb94_dNve1xY3rI8WTm4Gw5aEJlkMYV3U8,4198
147
+ alita_sdk/tools/azure_ai/search/__init__.py,sha256=H2HqPOYM-bf9xHzFU7GG0jT7oUlYst5SSxWxiAgS_6c,4751
148
148
  alita_sdk/tools/azure_ai/search/api_wrapper.py,sha256=E4p6HPDlwgxfT_i6cvg9rN4Vn_47CVAyNBAKLIGq3mU,7265
149
149
  alita_sdk/tools/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  alita_sdk/tools/base/tool.py,sha256=-N27AodZS49vdPCgFkU-bFS9bxoPopZBnNrmwInx3d0,864
151
- alita_sdk/tools/bitbucket/__init__.py,sha256=pYwgq-74AwZWeTIzdUKn-7tnuUgCqpOmz_2p-dBgEDs,4326
151
+ alita_sdk/tools/bitbucket/__init__.py,sha256=mPdq25Ib4yP1SV4g1dgK3WD3y-lKyuaNCmcIZTo893w,4355
152
152
  alita_sdk/tools/bitbucket/api_wrapper.py,sha256=HAmxY43VwfI08u0J6Z3ViBvADGNKM6LKAj1Iq5DNvMU,7133
153
153
  alita_sdk/tools/bitbucket/bitbucket_constants.py,sha256=UsbhQ1iEvrKoxceTFPWTYhaXS1zSxbmjs1TwY0-P4gw,462
154
154
  alita_sdk/tools/bitbucket/cloud_api_wrapper.py,sha256=9pvU--c02EB2JVkXuMI4cehWU657HsFpywAmtLC3yVs,7731
@@ -166,10 +166,10 @@ alita_sdk/tools/carrier/backend_tests_tool.py,sha256=Y1Va-VxDtnbRvrEdlwe63t-oDvq
166
166
  alita_sdk/tools/carrier/cancel_ui_test_tool.py,sha256=pD1sKEcZGBWJqFpgjeohMk93uuUPWruVJRPVVg90rpo,6438
167
167
  alita_sdk/tools/carrier/carrier_sdk.py,sha256=wb3d1W6dvo5NRLMiJcBcKy663J6IkbTwpKFTZX30QFQ,13672
168
168
  alita_sdk/tools/carrier/create_ui_excel_report_tool.py,sha256=8aSpkyIGXsOBTo8Ye_6p19v8OOl1y7QW47IJxZ6QDgM,20163
169
- alita_sdk/tools/carrier/create_ui_test_tool.py,sha256=sHi7-D1uqIUHEyoywI92h6MdUVybKfBXs_XttTu-Ck4,8624
169
+ alita_sdk/tools/carrier/create_ui_test_tool.py,sha256=knKvPOo9usI2XHqZtcbBEBzKwB9tS7GEl9KIX78vJiA,8184
170
170
  alita_sdk/tools/carrier/excel_reporter.py,sha256=fXptz7iaBDBcFSc8Ah8nZ9CSgugTONc5JMC1XcQEnfM,21487
171
171
  alita_sdk/tools/carrier/lighthouse_excel_reporter.py,sha256=mVuU63tl2n-Gntx9RuedjEU0U5AP1APKsSx1DvJs7wk,6684
172
- alita_sdk/tools/carrier/run_ui_test_tool.py,sha256=vmEtP-cpnOCyLLZfbw9Nq2ItGUFlcnsf1LmI_XyLrM8,21053
172
+ alita_sdk/tools/carrier/run_ui_test_tool.py,sha256=Wqfxi_jyOU6XxYGsTje2ftgm8O7PJRXRDHUwWcw8opM,26277
173
173
  alita_sdk/tools/carrier/tickets_tool.py,sha256=d-wFyFWWTvV01o-hyssb2S-oLnr51b6tlNTUqA_CohY,8099
174
174
  alita_sdk/tools/carrier/tools.py,sha256=cCLYcNzC_z0-AaqB46fyt7iOeP20LStVQKI5Dg1zWA4,1588
175
175
  alita_sdk/tools/carrier/ui_reports_tool.py,sha256=Y6EstTRCa9d11ipFUFGOYlpiEhFx7aOQcgZ_M5Gd1lQ,13708
@@ -216,8 +216,8 @@ alita_sdk/tools/code/linter/api_wrapper.py,sha256=wylpwhAw02Jt8L18CqBq2He5PbwIkx
216
216
  alita_sdk/tools/code/loaders/codesearcher.py,sha256=XoXXZtIQZhvjIwZlnl_4wVGHC-3saYzFo5oDR_Zh3EY,529
217
217
  alita_sdk/tools/code/sonar/__init__.py,sha256=u8wpgXJ_shToLl3G9-XEtGDor5dhmsnurIImh1-e-U0,3165
218
218
  alita_sdk/tools/code/sonar/api_wrapper.py,sha256=nNqxcWN_6W8c0ckj-Er9HkNuAdgQLoWBXh5UyzNutis,2653
219
- alita_sdk/tools/confluence/__init__.py,sha256=MkAUfSgQTZ6lVOHErwFggA4v_ctD9ADhhq6dKIALko0,6922
220
- alita_sdk/tools/confluence/api_wrapper.py,sha256=nCU4wAh3f6EN3NVXya3BVZQX47mi7JYAzylxJJOvMMc,88725
219
+ alita_sdk/tools/confluence/__init__.py,sha256=1wGSP4CjPbfpdZLsjC1SVftTV7XSvW3QCAMHuh9tIxA,6885
220
+ alita_sdk/tools/confluence/api_wrapper.py,sha256=zP8S85oVulhqrom1wlgelN-TSuKtPuYxwwGAlOSn_I0,88776
221
221
  alita_sdk/tools/confluence/loader.py,sha256=aHqgdIQMqkyRry8feHAhyd-a_ASEyW3JrV6epTRG6-c,9162
222
222
  alita_sdk/tools/confluence/utils.py,sha256=Lxo6dBD0OlvM4o0JuK6qeB_4LV9BptiwJA9e1vqNcDw,435
223
223
  alita_sdk/tools/custom_open_api/__init__.py,sha256=9aT5SPNPWcJC6jMZEM-3rUCXVULj_3-qJLQKmnreKNo,2537
@@ -244,7 +244,7 @@ alita_sdk/tools/gmail/gmail_wrapper.py,sha256=t0IYM3zb77Ub8o9kv6HugNm_OoG5tN9T73
244
244
  alita_sdk/tools/gmail/utils.py,sha256=cu6pbSsyMIr1BQOSs9et1rbAkk-Z_u48PB9FtJwFhUs,448
245
245
  alita_sdk/tools/google_places/__init__.py,sha256=mHKc7u9P2gqGDzqqJNQC9qiZYEm5gncnM_1XjtrM17o,3152
246
246
  alita_sdk/tools/google_places/api_wrapper.py,sha256=7nZly6nk4f4Tm7s2MVdnnwlb-1_WHRrDhyjDiqoyPjA,4674
247
- alita_sdk/tools/jira/__init__.py,sha256=es7-lRkd3MPwR4rXWTQTFI_yfP2hRoeD8oJD5SbhJkk,5978
247
+ alita_sdk/tools/jira/__init__.py,sha256=skVJwk54q59tQrWqVZlKuckGjI3m8euZJWJB86o3F9Y,5940
248
248
  alita_sdk/tools/jira/api_wrapper.py,sha256=i0TIHhVnh44dAVTt6RWrZ4o9hyhSMfVkYtk6FB3D2zA,62035
249
249
  alita_sdk/tools/keycloak/__init__.py,sha256=0WB9yXMUUAHQRni1ghDEmd7GYa7aJPsTVlZgMCM9cQ0,3050
250
250
  alita_sdk/tools/keycloak/api_wrapper.py,sha256=cOGr0f3S3-c6tRDBWI8wMnetjoNSxiV5rvC_0VHb8uw,3100
@@ -326,8 +326,8 @@ alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=UHVQUVqcBc3SZvDfO78HSuBzwAsRw
326
326
  alita_sdk/tools/zephyr_squad/__init__.py,sha256=rq4jOb3lRW2GXvAguk4H1KinO5f-zpygzhBJf-E1Ucw,2773
327
327
  alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=iOMxyE7vOc_LwFB_nBMiSFXkNtvbptA4i-BrTlo7M0A,5854
328
328
  alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=IYUJoMFOMA70knLhLtAnuGoy3OK80RuqeQZ710oyIxE,3631
329
- alita_sdk-0.3.174.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
330
- alita_sdk-0.3.174.dist-info/METADATA,sha256=tbM4oo5f1XOezac1kgzmW98CVraz5Yky6q6N82k_vKg,18757
331
- alita_sdk-0.3.174.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
332
- alita_sdk-0.3.174.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
333
- alita_sdk-0.3.174.dist-info/RECORD,,
329
+ alita_sdk-0.3.176.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
330
+ alita_sdk-0.3.176.dist-info/METADATA,sha256=0TeMFPCFp22pMbxrY1OgvQt-GhpOzAZwNVhvhw17Wcw,18810
331
+ alita_sdk-0.3.176.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
332
+ alita_sdk-0.3.176.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
333
+ alita_sdk-0.3.176.dist-info/RECORD,,