alita-sdk 0.3.554__py3-none-any.whl → 0.3.602__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.
Potentially problematic release.
This version of alita-sdk might be problematic. Click here for more details.
- alita_sdk/cli/agent_executor.py +2 -1
- alita_sdk/cli/agent_loader.py +34 -4
- alita_sdk/cli/agents.py +433 -203
- alita_sdk/configurations/openapi.py +227 -15
- alita_sdk/runtime/clients/client.py +4 -2
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +61 -11
- alita_sdk/runtime/langchain/constants.py +419 -171
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -2
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
- alita_sdk/runtime/langchain/langraph_agent.py +106 -21
- alita_sdk/runtime/langchain/utils.py +30 -14
- alita_sdk/runtime/toolkits/__init__.py +3 -0
- alita_sdk/runtime/toolkits/artifact.py +2 -1
- alita_sdk/runtime/toolkits/mcp.py +6 -3
- alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
- alita_sdk/runtime/toolkits/skill_router.py +2 -2
- alita_sdk/runtime/toolkits/tools.py +64 -2
- alita_sdk/runtime/toolkits/vectorstore.py +1 -1
- alita_sdk/runtime/tools/artifact.py +15 -0
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/llm.py +30 -11
- alita_sdk/runtime/tools/mcp_server_tool.py +6 -3
- alita_sdk/runtime/tools/router.py +2 -4
- alita_sdk/runtime/tools/sandbox.py +9 -6
- alita_sdk/runtime/utils/constants.py +5 -1
- alita_sdk/runtime/utils/mcp_client.py +1 -1
- alita_sdk/runtime/utils/mcp_sse_client.py +1 -1
- alita_sdk/runtime/utils/toolkit_utils.py +2 -0
- alita_sdk/tools/__init__.py +3 -1
- alita_sdk/tools/ado/repos/__init__.py +26 -8
- alita_sdk/tools/ado/repos/repos_wrapper.py +78 -52
- alita_sdk/tools/ado/test_plan/__init__.py +3 -2
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
- alita_sdk/tools/ado/utils.py +1 -18
- alita_sdk/tools/ado/wiki/__init__.py +2 -1
- alita_sdk/tools/ado/wiki/ado_wrapper.py +23 -1
- alita_sdk/tools/ado/work_item/__init__.py +3 -2
- alita_sdk/tools/ado/work_item/ado_wrapper.py +23 -1
- alita_sdk/tools/advanced_jira_mining/__init__.py +2 -1
- alita_sdk/tools/aws/delta_lake/__init__.py +2 -1
- alita_sdk/tools/azure_ai/search/__init__.py +2 -1
- alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
- alita_sdk/tools/base_indexer_toolkit.py +15 -6
- alita_sdk/tools/bitbucket/__init__.py +2 -1
- alita_sdk/tools/bitbucket/api_wrapper.py +1 -1
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +3 -3
- alita_sdk/tools/browser/__init__.py +1 -1
- alita_sdk/tools/carrier/__init__.py +1 -1
- alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
- alita_sdk/tools/cloud/aws/__init__.py +2 -1
- alita_sdk/tools/cloud/azure/__init__.py +2 -1
- alita_sdk/tools/cloud/gcp/__init__.py +2 -1
- alita_sdk/tools/cloud/k8s/__init__.py +2 -1
- alita_sdk/tools/code/linter/__init__.py +2 -1
- alita_sdk/tools/code/sonar/__init__.py +2 -1
- alita_sdk/tools/code_indexer_toolkit.py +19 -2
- alita_sdk/tools/confluence/__init__.py +7 -6
- alita_sdk/tools/confluence/api_wrapper.py +2 -2
- alita_sdk/tools/custom_open_api/__init__.py +2 -1
- alita_sdk/tools/elastic/__init__.py +2 -1
- alita_sdk/tools/elitea_base.py +28 -9
- alita_sdk/tools/figma/__init__.py +52 -6
- alita_sdk/tools/figma/api_wrapper.py +1158 -123
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +2 -1
- alita_sdk/tools/github/github_client.py +56 -92
- alita_sdk/tools/github/schemas.py +4 -4
- alita_sdk/tools/gitlab/__init__.py +2 -1
- alita_sdk/tools/gitlab/api_wrapper.py +118 -38
- alita_sdk/tools/gitlab_org/__init__.py +2 -1
- alita_sdk/tools/gitlab_org/api_wrapper.py +60 -62
- alita_sdk/tools/google/bigquery/__init__.py +2 -1
- alita_sdk/tools/google_places/__init__.py +2 -1
- alita_sdk/tools/jira/__init__.py +2 -1
- alita_sdk/tools/keycloak/__init__.py +2 -1
- alita_sdk/tools/localgit/__init__.py +2 -1
- alita_sdk/tools/memory/__init__.py +1 -1
- alita_sdk/tools/ocr/__init__.py +2 -1
- alita_sdk/tools/openapi/__init__.py +227 -15
- alita_sdk/tools/openapi/api_wrapper.py +1287 -802
- alita_sdk/tools/pandas/__init__.py +11 -5
- alita_sdk/tools/pandas/api_wrapper.py +38 -25
- alita_sdk/tools/postman/__init__.py +2 -1
- alita_sdk/tools/pptx/__init__.py +2 -1
- alita_sdk/tools/qtest/__init__.py +21 -2
- alita_sdk/tools/qtest/api_wrapper.py +430 -13
- alita_sdk/tools/rally/__init__.py +2 -1
- alita_sdk/tools/rally/api_wrapper.py +1 -1
- alita_sdk/tools/report_portal/__init__.py +2 -1
- alita_sdk/tools/salesforce/__init__.py +2 -1
- alita_sdk/tools/servicenow/__init__.py +2 -1
- alita_sdk/tools/sharepoint/__init__.py +2 -1
- alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
- alita_sdk/tools/slack/__init__.py +3 -2
- alita_sdk/tools/slack/api_wrapper.py +2 -2
- alita_sdk/tools/sql/__init__.py +3 -2
- alita_sdk/tools/testio/__init__.py +2 -1
- alita_sdk/tools/testrail/__init__.py +2 -1
- alita_sdk/tools/utils/content_parser.py +77 -3
- alita_sdk/tools/utils/text_operations.py +163 -71
- alita_sdk/tools/xray/__init__.py +3 -2
- alita_sdk/tools/yagmail/__init__.py +2 -1
- alita_sdk/tools/zephyr/__init__.py +2 -1
- alita_sdk/tools/zephyr_enterprise/__init__.py +2 -1
- alita_sdk/tools/zephyr_essential/__init__.py +2 -1
- alita_sdk/tools/zephyr_scale/__init__.py +3 -2
- alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
- alita_sdk/tools/zephyr_squad/__init__.py +2 -1
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/METADATA +7 -6
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/RECORD +116 -111
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/entry_points.txt +0 -0
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.602.dist-info}/top_level.txt +0 -0
|
@@ -23,7 +23,8 @@ from langchain_core.tools import ToolException
|
|
|
23
23
|
from msrest.authentication import BasicAuthentication
|
|
24
24
|
from pydantic import Field, PrivateAttr, create_model, model_validator, SecretStr
|
|
25
25
|
|
|
26
|
-
from
|
|
26
|
+
from ...elitea_base import BaseCodeToolApiWrapper
|
|
27
|
+
from ..utils import generate_diff, get_content_from_generator
|
|
27
28
|
from ...code_indexer_toolkit import CodeIndexerToolkit
|
|
28
29
|
from ...utils.available_tools_decorator import extend_with_parent_available_tools
|
|
29
30
|
|
|
@@ -129,9 +130,30 @@ class ArgsSchema(Enum):
|
|
|
129
130
|
)
|
|
130
131
|
UpdateFile = create_model(
|
|
131
132
|
"UpdateFile",
|
|
132
|
-
branch_name=(
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
branch_name=(
|
|
134
|
+
str,
|
|
135
|
+
Field(description="The name of the branch, e.g. `my_branch`.")
|
|
136
|
+
),
|
|
137
|
+
file_path=(
|
|
138
|
+
str,
|
|
139
|
+
Field(description="Path of a file to be updated."),
|
|
140
|
+
),
|
|
141
|
+
update_query=(
|
|
142
|
+
str,
|
|
143
|
+
Field(
|
|
144
|
+
description=(
|
|
145
|
+
"The exact OLD text to be replaced and the NEW "
|
|
146
|
+
"text to insert, using one or more block pairs like:"
|
|
147
|
+
"OLD <<<<Hello Earth!>>>> OLD NEW <<<<Hello Mars!>>>> NEW"
|
|
148
|
+
"Each OLD block must contain the exact text that will be replaced "
|
|
149
|
+
"via string replacement (exact full match including non-written characters). Each corresponding NEW "
|
|
150
|
+
"block contains the replacement text. For multi-line changes, it is "
|
|
151
|
+
"preferred to provide several smaller OLD/NEW pairs rather than one "
|
|
152
|
+
"large block, so that each OLD block closely matches a contiguous "
|
|
153
|
+
"snippet from the file."
|
|
154
|
+
)
|
|
155
|
+
),
|
|
156
|
+
),
|
|
135
157
|
)
|
|
136
158
|
DeleteFile = create_model(
|
|
137
159
|
"DeleteFile",
|
|
@@ -252,6 +274,9 @@ class ReposApiWrapper(CodeIndexerToolkit):
|
|
|
252
274
|
token: Optional[SecretStr]
|
|
253
275
|
_client: Optional[GitClient] = PrivateAttr()
|
|
254
276
|
|
|
277
|
+
# Reuse common file helpers from BaseCodeToolApiWrapper
|
|
278
|
+
edit_file = BaseCodeToolApiWrapper.edit_file
|
|
279
|
+
|
|
255
280
|
class Config:
|
|
256
281
|
arbitrary_types_allowed = True
|
|
257
282
|
|
|
@@ -835,22 +860,50 @@ class ReposApiWrapper(CodeIndexerToolkit):
|
|
|
835
860
|
logger.error(msg)
|
|
836
861
|
return ToolException(msg)
|
|
837
862
|
|
|
838
|
-
def
|
|
863
|
+
def _write_file(self, file_path: str, content: str, branch: str = None, commit_message: str = None) -> str:
|
|
864
|
+
"""Write content to a file in Azure DevOps by creating an edit commit.
|
|
865
|
+
|
|
866
|
+
This implementation follows the previous `update_file` behavior: it always
|
|
867
|
+
performs an 'edit' change (does not create the file), gets the latest
|
|
868
|
+
commit id for the branch and pushes a new commit containing the change.
|
|
839
869
|
"""
|
|
840
|
-
|
|
870
|
+
try:
|
|
871
|
+
# Get the latest commit ID of the target branch
|
|
872
|
+
branch_obj = self._client.get_branch(
|
|
873
|
+
repository_id=self.repository_id,
|
|
874
|
+
project=self.project,
|
|
875
|
+
name=branch,
|
|
876
|
+
)
|
|
877
|
+
if branch_obj is None or not hasattr(branch_obj, 'commit') or not hasattr(branch_obj.commit, 'commit_id'):
|
|
878
|
+
raise ToolException(f"Branch `{branch}` does not exist or has no commits.")
|
|
879
|
+
|
|
880
|
+
latest_commit_id = branch_obj.commit.commit_id
|
|
881
|
+
|
|
882
|
+
# Build edit change and push
|
|
883
|
+
change = GitChange("edit", file_path, content).to_dict()
|
|
884
|
+
|
|
885
|
+
ref_update = GitRefUpdate(name=f"refs/heads/{branch}", old_object_id=latest_commit_id)
|
|
886
|
+
new_commit = GitCommit(comment=commit_message or ("Update " + file_path), changes=[change])
|
|
887
|
+
push = GitPush(commits=[new_commit], ref_updates=[ref_update])
|
|
888
|
+
|
|
889
|
+
self._client.create_push(push=push, repository_id=self.repository_id, project=self.project)
|
|
890
|
+
return f"Updated file {file_path}"
|
|
891
|
+
except ToolException:
|
|
892
|
+
# Re-raise known tool exceptions
|
|
893
|
+
raise
|
|
894
|
+
except Exception as e:
|
|
895
|
+
logger.error(f"Unable to write file {file_path}: {e}")
|
|
896
|
+
raise ToolException(f"Unable to write file {file_path}: {str(e)}")
|
|
897
|
+
|
|
898
|
+
def update_file(self, branch_name: str, file_path: str, update_query: str) -> str:
|
|
899
|
+
"""Updates a file with new content in Azure DevOps using OLD/NEW markers.
|
|
900
|
+
|
|
841
901
|
Parameters:
|
|
842
902
|
branch_name (str): The name of the branch where update the file.
|
|
843
903
|
file_path (str): Path to the file for update.
|
|
844
|
-
update_query(str): Contains the file contents
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
For example:
|
|
848
|
-
OLD <<<<
|
|
849
|
-
Hello Earth!
|
|
850
|
-
>>>> OLD
|
|
851
|
-
NEW <<<<
|
|
852
|
-
Hello Mars!
|
|
853
|
-
>>>> NEW
|
|
904
|
+
update_query(str): Contains the file contents required to be updated,
|
|
905
|
+
wrapped in Git-style OLD/NEW blocks.
|
|
906
|
+
|
|
854
907
|
Returns:
|
|
855
908
|
A success or failure message
|
|
856
909
|
"""
|
|
@@ -863,43 +916,16 @@ class ReposApiWrapper(CodeIndexerToolkit):
|
|
|
863
916
|
"Please create a new branch and try again."
|
|
864
917
|
)
|
|
865
918
|
try:
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
if not old.strip():
|
|
874
|
-
continue
|
|
875
|
-
updated_file_content = updated_file_content.replace(old, new)
|
|
876
|
-
|
|
877
|
-
if file_content == updated_file_content:
|
|
878
|
-
return (
|
|
879
|
-
"File content was not updated because old content was not found or empty. "
|
|
880
|
-
"It may be helpful to use the read_file action to get "
|
|
881
|
-
"the current file contents."
|
|
882
|
-
)
|
|
883
|
-
|
|
884
|
-
# Get the latest commit ID of the active branch to use as oldObjectId
|
|
885
|
-
branch = self._client.get_branch(
|
|
886
|
-
repository_id=self.repository_id,
|
|
887
|
-
project=self.project,
|
|
888
|
-
name=self.active_branch,
|
|
889
|
-
)
|
|
890
|
-
latest_commit_id = branch.commit.commit_id
|
|
891
|
-
|
|
892
|
-
change = GitChange("edit", file_path, updated_file_content).to_dict()
|
|
893
|
-
|
|
894
|
-
ref_update = GitRefUpdate(
|
|
895
|
-
name=f"refs/heads/{self.active_branch}", old_object_id=latest_commit_id
|
|
896
|
-
)
|
|
897
|
-
new_commit = GitCommit(comment=f"Update {file_path}", changes=[change])
|
|
898
|
-
push = GitPush(commits=[new_commit], ref_updates=[ref_update])
|
|
899
|
-
self._client.create_push(
|
|
900
|
-
push=push, repository_id=self.repository_id, project=self.project
|
|
919
|
+
# Let edit_file handle parsing and content updates; this will call _read_file and _write_file.
|
|
920
|
+
# For ADO, branch_name is used as branch; commit message is derived from file_path.
|
|
921
|
+
return self.edit_file(
|
|
922
|
+
file_path=file_path,
|
|
923
|
+
file_query=update_query,
|
|
924
|
+
branch=self.active_branch,
|
|
925
|
+
commit_message=f"Update {file_path}",
|
|
901
926
|
)
|
|
902
|
-
|
|
927
|
+
except ToolException as e:
|
|
928
|
+
return str(e)
|
|
903
929
|
except Exception as e:
|
|
904
930
|
msg = f"Unable to update file due to error:\n{str(e)}"
|
|
905
931
|
logger.error(msg)
|
|
@@ -11,6 +11,7 @@ from ....configurations.pgvector import PgVectorConfiguration
|
|
|
11
11
|
from .test_plan_wrapper import TestPlanApiWrapper
|
|
12
12
|
from ...base.tool import BaseAction
|
|
13
13
|
from ...utils import clean_string, get_max_toolkit_length, check_connection_response
|
|
14
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
name = "azure_devops_plans"
|
|
@@ -40,7 +41,7 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
|
|
|
40
41
|
m = create_model(
|
|
41
42
|
name_alias,
|
|
42
43
|
ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
|
|
43
|
-
limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
|
|
44
|
+
limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5, gt=0)),
|
|
44
45
|
# indexer settings
|
|
45
46
|
pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None,
|
|
46
47
|
description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
|
|
@@ -122,7 +123,7 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
|
|
|
122
123
|
name=tool["name"],
|
|
123
124
|
description=description,
|
|
124
125
|
args_schema=tool["args_schema"],
|
|
125
|
-
metadata={
|
|
126
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
126
127
|
))
|
|
127
128
|
return cls(tools=tools)
|
|
128
129
|
|
|
@@ -180,7 +180,29 @@ class TestPlanApiWrapper(NonCodeIndexerToolkit):
|
|
|
180
180
|
connection = Connection(base_url=values['organization_url'], creds=credentials)
|
|
181
181
|
cls._client = connection.clients.get_test_plan_client()
|
|
182
182
|
except Exception as e:
|
|
183
|
-
|
|
183
|
+
error_msg = str(e).lower()
|
|
184
|
+
if "expired" in error_msg or "token" in error_msg and ("invalid" in error_msg or "unauthorized" in error_msg):
|
|
185
|
+
raise ValueError(
|
|
186
|
+
"Azure DevOps connection failed: Your access token has expired or is invalid. "
|
|
187
|
+
"Please refresh your token in the toolkit configuration."
|
|
188
|
+
)
|
|
189
|
+
elif "401" in error_msg or "unauthorized" in error_msg:
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"Azure DevOps connection failed: Authentication failed. "
|
|
192
|
+
"Please check your credentials in the toolkit configuration."
|
|
193
|
+
)
|
|
194
|
+
elif "404" in error_msg or "not found" in error_msg:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
"Azure DevOps connection failed: Organization or project not found. "
|
|
197
|
+
"Please verify your organization URL and project name."
|
|
198
|
+
)
|
|
199
|
+
elif "timeout" in error_msg or "timed out" in error_msg:
|
|
200
|
+
raise ValueError(
|
|
201
|
+
"Azure DevOps connection failed: Connection timed out. "
|
|
202
|
+
"Please check your network connection and try again."
|
|
203
|
+
)
|
|
204
|
+
else:
|
|
205
|
+
raise ValueError(f"Azure DevOps connection failed: {e}")
|
|
184
206
|
return super().validate_toolkit(values)
|
|
185
207
|
|
|
186
208
|
def create_test_plan(self, test_plan_create_params: str):
|
alita_sdk/tools/ado/utils.py
CHANGED
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import difflib
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def extract_old_new_pairs(file_query: str):
|
|
6
|
-
"""
|
|
7
|
-
Extracts old and new content pairs from a file query.
|
|
8
|
-
Parameters:
|
|
9
|
-
file_query (str): The file query containing old and new content.
|
|
10
|
-
Returns:
|
|
11
|
-
list of tuples: A list where each tuple contains (old_content, new_content).
|
|
12
|
-
"""
|
|
13
|
-
old_pattern = re.compile(r"OLD <<<<\s*(.*?)\s*>>>> OLD", re.DOTALL)
|
|
14
|
-
new_pattern = re.compile(r"NEW <<<<\s*(.*?)\s*>>>> NEW", re.DOTALL)
|
|
15
|
-
|
|
16
|
-
old_contents = old_pattern.findall(file_query)
|
|
17
|
-
new_contents = new_pattern.findall(file_query)
|
|
18
|
-
|
|
19
|
-
return list(zip(old_contents, new_contents))
|
|
20
|
-
|
|
21
|
-
|
|
22
4
|
def generate_diff(base_text, target_text, file_path):
|
|
23
5
|
base_lines = base_text.splitlines(keepends=True)
|
|
24
6
|
target_lines = target_text.splitlines(keepends=True)
|
|
@@ -28,6 +10,7 @@ def generate_diff(base_text, target_text, file_path):
|
|
|
28
10
|
|
|
29
11
|
return "".join(diff)
|
|
30
12
|
|
|
13
|
+
|
|
31
14
|
def get_content_from_generator(content_generator):
|
|
32
15
|
def safe_decode(chunk):
|
|
33
16
|
try:
|
|
@@ -10,6 +10,7 @@ from ....configurations.ado import AdoConfiguration
|
|
|
10
10
|
from ....configurations.pgvector import PgVectorConfiguration
|
|
11
11
|
from ...base.tool import BaseAction
|
|
12
12
|
from ...utils import clean_string, get_max_toolkit_length, check_connection_response
|
|
13
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
13
14
|
|
|
14
15
|
name = "azure_devops_wiki"
|
|
15
16
|
name_alias = 'ado_wiki'
|
|
@@ -116,7 +117,7 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
|
|
|
116
117
|
name=tool["name"],
|
|
117
118
|
description=description,
|
|
118
119
|
args_schema=tool["args_schema"],
|
|
119
|
-
metadata={
|
|
120
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
120
121
|
))
|
|
121
122
|
return cls(tools=tools)
|
|
122
123
|
|
|
@@ -105,7 +105,29 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
|
|
|
105
105
|
cls._core_client = connection.clients.get_core_client()
|
|
106
106
|
|
|
107
107
|
except Exception as e:
|
|
108
|
-
|
|
108
|
+
error_msg = str(e).lower()
|
|
109
|
+
if "expired" in error_msg or "token" in error_msg and ("invalid" in error_msg or "unauthorized" in error_msg):
|
|
110
|
+
raise ValueError(
|
|
111
|
+
"Azure DevOps connection failed: Your access token has expired or is invalid. "
|
|
112
|
+
"Please refresh your token in the toolkit configuration."
|
|
113
|
+
)
|
|
114
|
+
elif "401" in error_msg or "unauthorized" in error_msg:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"Azure DevOps connection failed: Authentication failed. "
|
|
117
|
+
"Please check your credentials in the toolkit configuration."
|
|
118
|
+
)
|
|
119
|
+
elif "404" in error_msg or "not found" in error_msg:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
"Azure DevOps connection failed: Organization or project not found. "
|
|
122
|
+
"Please verify your organization URL and project name."
|
|
123
|
+
)
|
|
124
|
+
elif "timeout" in error_msg or "timed out" in error_msg:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
"Azure DevOps connection failed: Connection timed out. "
|
|
127
|
+
"Please check your network connection and try again."
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
raise ValueError(f"Azure DevOps connection failed: {e}")
|
|
109
131
|
|
|
110
132
|
return super().validate_toolkit(values)
|
|
111
133
|
|
|
@@ -10,6 +10,7 @@ from ....configurations.ado import AdoConfiguration
|
|
|
10
10
|
from ....configurations.pgvector import PgVectorConfiguration
|
|
11
11
|
from ...base.tool import BaseAction
|
|
12
12
|
from ...utils import clean_string, get_max_toolkit_length, check_connection_response
|
|
13
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
13
14
|
|
|
14
15
|
name = "ado_boards"
|
|
15
16
|
|
|
@@ -37,7 +38,7 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
|
|
|
37
38
|
m = create_model(
|
|
38
39
|
name,
|
|
39
40
|
ado_configuration=(AdoConfiguration, Field(description="Ado Work Item configuration", json_schema_extra={'configuration_types': ['ado']})),
|
|
40
|
-
limit=(Optional[int], Field(description="ADO
|
|
41
|
+
limit=(Optional[int], Field(description="Default ADO boards result limit (can be overridden by agent instructions)", default=5, gt=0)),
|
|
41
42
|
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
|
42
43
|
# indexer settings
|
|
43
44
|
pgvector_configuration=(Optional[PgVectorConfiguration], Field(default = None,
|
|
@@ -117,7 +118,7 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
|
|
|
117
118
|
name=tool["name"],
|
|
118
119
|
description=description,
|
|
119
120
|
args_schema=tool["args_schema"],
|
|
120
|
-
metadata={
|
|
121
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
121
122
|
))
|
|
122
123
|
return cls(tools=tools)
|
|
123
124
|
|
|
@@ -127,7 +127,29 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
|
|
|
127
127
|
cls._core_client = connection.clients_v7_1.get_core_client()
|
|
128
128
|
|
|
129
129
|
except Exception as e:
|
|
130
|
-
|
|
130
|
+
error_msg = str(e).lower()
|
|
131
|
+
if "expired" in error_msg or "token" in error_msg and ("invalid" in error_msg or "unauthorized" in error_msg):
|
|
132
|
+
raise ValueError(
|
|
133
|
+
"Azure DevOps connection failed: Your access token has expired or is invalid. "
|
|
134
|
+
"Please refresh your token in the toolkit configuration."
|
|
135
|
+
)
|
|
136
|
+
elif "401" in error_msg or "unauthorized" in error_msg:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Azure DevOps connection failed: Authentication failed. "
|
|
139
|
+
"Please check your credentials in the toolkit configuration."
|
|
140
|
+
)
|
|
141
|
+
elif "404" in error_msg or "not found" in error_msg:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
"Azure DevOps connection failed: Organization or project not found. "
|
|
144
|
+
"Please verify your organization URL and project name."
|
|
145
|
+
)
|
|
146
|
+
elif "timeout" in error_msg or "timed out" in error_msg:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
"Azure DevOps connection failed: Connection timed out. "
|
|
149
|
+
"Please check your network connection and try again."
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError(f"Azure DevOps connection failed: {e}")
|
|
131
153
|
|
|
132
154
|
return super().validate_toolkit(values)
|
|
133
155
|
|
|
@@ -7,6 +7,7 @@ from .data_mining_wrapper import AdvancedJiraMiningWrapper
|
|
|
7
7
|
from ..base.tool import BaseAction
|
|
8
8
|
from ..elitea_base import filter_missconfigured_index_tools
|
|
9
9
|
from ..utils import clean_string, get_max_toolkit_length
|
|
10
|
+
from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
10
11
|
|
|
11
12
|
name = "advanced_jira_mining"
|
|
12
13
|
|
|
@@ -78,7 +79,7 @@ class AdvancedJiraMiningToolkit(BaseToolkit):
|
|
|
78
79
|
name=tool["name"],
|
|
79
80
|
description=description,
|
|
80
81
|
args_schema=tool["args_schema"],
|
|
81
|
-
metadata={
|
|
82
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
82
83
|
))
|
|
83
84
|
return cls(tools=tools)
|
|
84
85
|
|
|
@@ -9,6 +9,7 @@ from alita_sdk.configurations.delta_lake import DeltaLakeConfiguration
|
|
|
9
9
|
from ...utils import clean_string, get_max_toolkit_length
|
|
10
10
|
from .api_wrapper import DeltaLakeApiWrapper
|
|
11
11
|
from .tool import DeltaLakeAction
|
|
12
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
12
13
|
|
|
13
14
|
name = "delta_lake"
|
|
14
15
|
|
|
@@ -126,7 +127,7 @@ class DeltaLakeToolkit(BaseToolkit):
|
|
|
126
127
|
name=t["name"],
|
|
127
128
|
description=description,
|
|
128
129
|
args_schema=t["args_schema"],
|
|
129
|
-
metadata={
|
|
130
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: t["name"]} if toolkit_name else {TOOL_NAME_META: t["name"]}
|
|
130
131
|
)
|
|
131
132
|
)
|
|
132
133
|
return instance
|
|
@@ -9,6 +9,7 @@ from ...elitea_base import filter_missconfigured_index_tools
|
|
|
9
9
|
from ...utils import clean_string, get_max_toolkit_length, check_connection_response
|
|
10
10
|
from ....configurations.azure_search import AzureSearchConfiguration
|
|
11
11
|
import requests
|
|
12
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
12
13
|
|
|
13
14
|
logger = getLogger(__name__)
|
|
14
15
|
|
|
@@ -91,7 +92,7 @@ class AzureSearchToolkit(BaseToolkit):
|
|
|
91
92
|
name=tool["name"],
|
|
92
93
|
description=description,
|
|
93
94
|
args_schema=tool["args_schema"],
|
|
94
|
-
metadata={
|
|
95
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
95
96
|
))
|
|
96
97
|
return cls(tools=tools)
|
|
97
98
|
|
|
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
|
|
|
11
11
|
|
|
12
12
|
class AzureSearchInput(BaseModel):
|
|
13
13
|
search_text: str = Field(..., description="The text to search for in the Azure Search index.")
|
|
14
|
-
limit: int = Field(10, description="The number of results to return.")
|
|
14
|
+
limit: int = Field(10, description="The number of results to return.", gt=0)
|
|
15
15
|
selected_fields: Optional[List[str]] = Field(None, description="The fields to retrieve from the document.")
|
|
16
16
|
|
|
17
17
|
class AzureDocumentInput(BaseModel):
|
|
@@ -46,7 +46,7 @@ BaseSearchParams = create_model(
|
|
|
46
46
|
examples=["{\"key\": \"value\"}", "{\"status\": \"active\"}"]
|
|
47
47
|
)),
|
|
48
48
|
cut_off=(Optional[float], Field(description="Cut-off score for search results", default=DEFAULT_CUT_OFF, ge=0, le=1)),
|
|
49
|
-
search_top=(Optional[int], Field(description="Number of top results to return", default=10)),
|
|
49
|
+
search_top=(Optional[int], Field(description="Number of top results to return", default=10, gt=0)),
|
|
50
50
|
full_text_search=(Optional[Dict[str, Any]], Field(
|
|
51
51
|
description="Full text search parameters. Can be a dictionary with search options.",
|
|
52
52
|
default=None
|
|
@@ -76,7 +76,7 @@ BaseStepbackSearchParams = create_model(
|
|
|
76
76
|
examples=["{\"key\": \"value\"}", "{\"status\": \"active\"}"]
|
|
77
77
|
)),
|
|
78
78
|
cut_off=(Optional[float], Field(description="Cut-off score for search results", default=DEFAULT_CUT_OFF, ge=0, le=1)),
|
|
79
|
-
search_top=(Optional[int], Field(description="Number of top results to return", default=10)),
|
|
79
|
+
search_top=(Optional[int], Field(description="Number of top results to return", default=10, gt=0)),
|
|
80
80
|
full_text_search=(Optional[Dict[str, Any]], Field(
|
|
81
81
|
description="Full text search parameters. Can be a dictionary with search options.",
|
|
82
82
|
default=None
|
|
@@ -186,7 +186,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
|
|
|
186
186
|
#
|
|
187
187
|
results_count = result["count"]
|
|
188
188
|
# Final update should always be forced
|
|
189
|
-
self.index_meta_update(index_name, IndexerKeywords.INDEX_META_COMPLETED.value, results_count, update_force=True)
|
|
189
|
+
self.index_meta_update(index_name, IndexerKeywords.INDEX_META_COMPLETED.value, results_count, update_force=True, error=None)
|
|
190
190
|
self._emit_index_event(index_name)
|
|
191
191
|
#
|
|
192
192
|
return {"status": "ok", "message": f"successfully indexed {results_count} documents" if results_count > 0
|
|
@@ -195,8 +195,8 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
|
|
|
195
195
|
# Do maximum effort at least send custom event for supposed changed status
|
|
196
196
|
msg = str(e)
|
|
197
197
|
try:
|
|
198
|
-
# Error update should also be forced
|
|
199
|
-
self.index_meta_update(index_name, IndexerKeywords.INDEX_META_FAILED.value, result["count"], update_force=True)
|
|
198
|
+
# Error update should also be forced and include the error message
|
|
199
|
+
self.index_meta_update(index_name, IndexerKeywords.INDEX_META_FAILED.value, result["count"], update_force=True, error=msg)
|
|
200
200
|
except Exception as ie:
|
|
201
201
|
logger.error(f"Failed to update index meta status to FAILED for index '{index_name}': {ie}")
|
|
202
202
|
msg = f"{msg}; additionally failed to update index meta status to FAILED: {ie}"
|
|
@@ -505,12 +505,14 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
|
|
|
505
505
|
"task_id": None,
|
|
506
506
|
"conversation_id": None,
|
|
507
507
|
"toolkit_id": self.toolkit_id,
|
|
508
|
+
# Initialize error field to keep track of the latest failure reason if any
|
|
509
|
+
"error": None,
|
|
508
510
|
}
|
|
509
511
|
metadata["history"] = json.dumps([metadata])
|
|
510
512
|
index_meta_doc = Document(page_content=f"{IndexerKeywords.INDEX_META_TYPE.value}_{index_name}", metadata=metadata)
|
|
511
513
|
add_documents(vectorstore=self.vectorstore, documents=[index_meta_doc])
|
|
512
514
|
|
|
513
|
-
def index_meta_update(self, index_name: str, state: str, result: int, update_force: bool = True, interval: Optional[float] = None):
|
|
515
|
+
def index_meta_update(self, index_name: str, state: str, result: int, update_force: bool = True, interval: Optional[float] = None, error: Optional[str] = None):
|
|
514
516
|
"""Update `index_meta` document with optional time-based throttling.
|
|
515
517
|
|
|
516
518
|
Args:
|
|
@@ -522,6 +524,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
|
|
|
522
524
|
interval: Optional custom interval (in seconds) for this call when `update_force` is `False`.
|
|
523
525
|
If `None`, falls back to the value stored in `self._index_meta_config["update_interval"]`
|
|
524
526
|
if present, otherwise uses `INDEX_META_UPDATE_INTERVAL`.
|
|
527
|
+
error: Optional error message to record when the state represents a failed index.
|
|
525
528
|
"""
|
|
526
529
|
self._ensure_vectorstore_initialized()
|
|
527
530
|
if not hasattr(self, "_index_meta_last_update_time"):
|
|
@@ -560,6 +563,12 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
|
|
|
560
563
|
metadata["updated"] = result
|
|
561
564
|
metadata["state"] = state
|
|
562
565
|
metadata["updated_on"] = time.time()
|
|
566
|
+
# Attach error if provided, else clear on success
|
|
567
|
+
if error is not None:
|
|
568
|
+
metadata["error"] = error
|
|
569
|
+
elif state == IndexerKeywords.INDEX_META_COMPLETED.value:
|
|
570
|
+
# Clear previous error on successful completion
|
|
571
|
+
metadata["error"] = None
|
|
563
572
|
#
|
|
564
573
|
history_raw = metadata.pop("history", "[]")
|
|
565
574
|
try:
|
|
@@ -13,6 +13,7 @@ from ..utils import clean_string, get_max_toolkit_length, check_connection_respo
|
|
|
13
13
|
from ...configurations.bitbucket import BitbucketConfiguration
|
|
14
14
|
from ...configurations.pgvector import PgVectorConfiguration
|
|
15
15
|
import requests
|
|
16
|
+
from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
name = "bitbucket"
|
|
@@ -114,7 +115,7 @@ class AlitaBitbucketToolkit(BaseToolkit):
|
|
|
114
115
|
name=tool["name"],
|
|
115
116
|
description=description,
|
|
116
117
|
args_schema=tool["args_schema"],
|
|
117
|
-
metadata={
|
|
118
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
118
119
|
))
|
|
119
120
|
return cls(tools=tools)
|
|
120
121
|
|
|
@@ -57,7 +57,7 @@ SetActiveBranchModel = create_model(
|
|
|
57
57
|
|
|
58
58
|
ListBranchesInRepoModel = create_model(
|
|
59
59
|
"ListBranchesInRepoModel",
|
|
60
|
-
limit=(Optional[int], Field(default=20, description="Maximum number of branches to return. If not provided, all branches will be returned.")),
|
|
60
|
+
limit=(Optional[int], Field(default=20, description="Maximum number of branches to return. If not provided, all branches will be returned.", gt=0)),
|
|
61
61
|
branch_wildcard=(Optional[str], Field(default=None, description="Wildcard pattern to filter branches by name. If not provided, all branches will be returned."))
|
|
62
62
|
)
|
|
63
63
|
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any, Dict, List
|
|
|
8
8
|
from atlassian.bitbucket import Bitbucket, Cloud
|
|
9
9
|
from langchain_core.tools import ToolException
|
|
10
10
|
from requests import Response
|
|
11
|
-
from ..
|
|
11
|
+
from ..utils.text_operations import parse_old_new_markers
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
logging.basicConfig(level=logging.DEBUG)
|
|
@@ -145,7 +145,7 @@ class BitbucketServerApi(BitbucketApiAbstract):
|
|
|
145
145
|
def update_file(self, file_path: str, update_query: str, branch: str) -> str:
|
|
146
146
|
file_content = self.get_file(file_path=file_path, branch=branch)
|
|
147
147
|
updated_file_content = file_content
|
|
148
|
-
for old, new in
|
|
148
|
+
for old, new in parse_old_new_markers(update_query):
|
|
149
149
|
if not old.strip():
|
|
150
150
|
continue
|
|
151
151
|
updated_file_content = updated_file_content.replace(old, new)
|
|
@@ -319,7 +319,7 @@ class BitbucketCloudApi(BitbucketApiAbstract):
|
|
|
319
319
|
|
|
320
320
|
file_content = self.get_file(file_path=file_path, branch=branch)
|
|
321
321
|
updated_file_content = file_content
|
|
322
|
-
for old, new in
|
|
322
|
+
for old, new in parse_old_new_markers(file_query=update_query):
|
|
323
323
|
if not old.strip():
|
|
324
324
|
continue
|
|
325
325
|
updated_file_content = updated_file_content.replace(old, new)
|
|
@@ -128,7 +128,7 @@ class BrowserToolkit(BaseToolkit):
|
|
|
128
128
|
if toolkit_name:
|
|
129
129
|
tool_entry.description = f"{tool_entry.description}\nToolkit: {toolkit_name}"
|
|
130
130
|
tool_entry.description = tool_entry.description[:1000]
|
|
131
|
-
tool_entry.metadata = {"toolkit_name": toolkit_name}
|
|
131
|
+
tool_entry.metadata = {"toolkit_name": toolkit_name, "toolkit_type": name}
|
|
132
132
|
tools.append(tool_entry)
|
|
133
133
|
return cls(tools=tools)
|
|
134
134
|
|
|
@@ -77,7 +77,7 @@ class AlitaCarrierToolkit(BaseToolkit):
|
|
|
77
77
|
if toolkit_name:
|
|
78
78
|
tool_instance.description = f"{tool_instance.description}\nToolkit: {toolkit_name}"
|
|
79
79
|
tool_instance.description = tool_instance.description[:1000]
|
|
80
|
-
tool_instance.metadata = {"toolkit_name": toolkit_name}
|
|
80
|
+
tool_instance.metadata = {"toolkit_name": toolkit_name, "toolkit_type": name}
|
|
81
81
|
tools.append(tool_instance)
|
|
82
82
|
logger.info(f"[AlitaCarrierToolkit] Successfully initialized tool '{tool_instance.name}'")
|
|
83
83
|
except Exception as e:
|