alita-sdk 0.3.486__py3-none-any.whl → 0.3.515__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.

Files changed (124) hide show
  1. alita_sdk/cli/agent_loader.py +27 -6
  2. alita_sdk/cli/agents.py +10 -1
  3. alita_sdk/cli/inventory.py +12 -195
  4. alita_sdk/cli/tools/filesystem.py +95 -9
  5. alita_sdk/community/inventory/__init__.py +12 -0
  6. alita_sdk/community/inventory/toolkit.py +9 -5
  7. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  8. alita_sdk/configurations/ado.py +144 -0
  9. alita_sdk/configurations/confluence.py +76 -42
  10. alita_sdk/configurations/figma.py +76 -0
  11. alita_sdk/configurations/gitlab.py +2 -0
  12. alita_sdk/configurations/qtest.py +72 -1
  13. alita_sdk/configurations/report_portal.py +96 -0
  14. alita_sdk/configurations/sharepoint.py +148 -0
  15. alita_sdk/configurations/testio.py +83 -0
  16. alita_sdk/runtime/clients/artifact.py +2 -2
  17. alita_sdk/runtime/clients/client.py +64 -40
  18. alita_sdk/runtime/clients/sandbox_client.py +14 -0
  19. alita_sdk/runtime/langchain/assistant.py +48 -2
  20. alita_sdk/runtime/langchain/constants.py +3 -1
  21. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  22. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  23. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +2 -1
  24. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  25. alita_sdk/runtime/langchain/langraph_agent.py +10 -10
  26. alita_sdk/runtime/langchain/utils.py +6 -1
  27. alita_sdk/runtime/toolkits/artifact.py +14 -5
  28. alita_sdk/runtime/toolkits/datasource.py +13 -6
  29. alita_sdk/runtime/toolkits/mcp.py +94 -219
  30. alita_sdk/runtime/toolkits/planning.py +13 -6
  31. alita_sdk/runtime/toolkits/tools.py +60 -25
  32. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  33. alita_sdk/runtime/tools/artifact.py +185 -23
  34. alita_sdk/runtime/tools/function.py +2 -1
  35. alita_sdk/runtime/tools/llm.py +155 -34
  36. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  37. alita_sdk/runtime/tools/mcp_server_tool.py +2 -4
  38. alita_sdk/runtime/tools/vectorstore_base.py +3 -3
  39. alita_sdk/runtime/utils/AlitaCallback.py +136 -21
  40. alita_sdk/runtime/utils/mcp_client.py +492 -0
  41. alita_sdk/runtime/utils/mcp_oauth.py +125 -8
  42. alita_sdk/runtime/utils/mcp_sse_client.py +35 -6
  43. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  44. alita_sdk/runtime/utils/toolkit_utils.py +7 -13
  45. alita_sdk/runtime/utils/utils.py +2 -0
  46. alita_sdk/tools/__init__.py +15 -0
  47. alita_sdk/tools/ado/repos/__init__.py +10 -12
  48. alita_sdk/tools/ado/test_plan/__init__.py +23 -8
  49. alita_sdk/tools/ado/wiki/__init__.py +24 -8
  50. alita_sdk/tools/ado/wiki/ado_wrapper.py +21 -7
  51. alita_sdk/tools/ado/work_item/__init__.py +24 -8
  52. alita_sdk/tools/advanced_jira_mining/__init__.py +10 -8
  53. alita_sdk/tools/aws/delta_lake/__init__.py +12 -9
  54. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  55. alita_sdk/tools/azure_ai/search/__init__.py +9 -7
  56. alita_sdk/tools/base/tool.py +5 -1
  57. alita_sdk/tools/base_indexer_toolkit.py +26 -1
  58. alita_sdk/tools/bitbucket/__init__.py +14 -10
  59. alita_sdk/tools/bitbucket/api_wrapper.py +50 -2
  60. alita_sdk/tools/browser/__init__.py +5 -4
  61. alita_sdk/tools/carrier/__init__.py +5 -6
  62. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  63. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +2 -0
  64. alita_sdk/tools/chunkers/universal_chunker.py +1 -0
  65. alita_sdk/tools/cloud/aws/__init__.py +9 -7
  66. alita_sdk/tools/cloud/azure/__init__.py +9 -7
  67. alita_sdk/tools/cloud/gcp/__init__.py +9 -7
  68. alita_sdk/tools/cloud/k8s/__init__.py +9 -7
  69. alita_sdk/tools/code/linter/__init__.py +9 -8
  70. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  71. alita_sdk/tools/code/sonar/__init__.py +9 -7
  72. alita_sdk/tools/confluence/__init__.py +15 -10
  73. alita_sdk/tools/confluence/api_wrapper.py +63 -14
  74. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  75. alita_sdk/tools/elastic/__init__.py +10 -8
  76. alita_sdk/tools/elitea_base.py +387 -9
  77. alita_sdk/tools/figma/__init__.py +8 -7
  78. alita_sdk/tools/github/__init__.py +12 -14
  79. alita_sdk/tools/github/github_client.py +68 -2
  80. alita_sdk/tools/github/tool.py +5 -1
  81. alita_sdk/tools/gitlab/__init__.py +14 -11
  82. alita_sdk/tools/gitlab/api_wrapper.py +81 -1
  83. alita_sdk/tools/gitlab_org/__init__.py +9 -8
  84. alita_sdk/tools/google/bigquery/__init__.py +12 -12
  85. alita_sdk/tools/google/bigquery/tool.py +5 -1
  86. alita_sdk/tools/google_places/__init__.py +9 -8
  87. alita_sdk/tools/jira/__init__.py +15 -10
  88. alita_sdk/tools/keycloak/__init__.py +10 -8
  89. alita_sdk/tools/localgit/__init__.py +8 -3
  90. alita_sdk/tools/localgit/local_git.py +62 -54
  91. alita_sdk/tools/localgit/tool.py +5 -1
  92. alita_sdk/tools/memory/__init__.py +11 -3
  93. alita_sdk/tools/ocr/__init__.py +10 -8
  94. alita_sdk/tools/openapi/__init__.py +6 -2
  95. alita_sdk/tools/pandas/__init__.py +9 -7
  96. alita_sdk/tools/postman/__init__.py +10 -11
  97. alita_sdk/tools/pptx/__init__.py +9 -9
  98. alita_sdk/tools/qtest/__init__.py +9 -8
  99. alita_sdk/tools/rally/__init__.py +9 -8
  100. alita_sdk/tools/report_portal/__init__.py +11 -9
  101. alita_sdk/tools/salesforce/__init__.py +9 -9
  102. alita_sdk/tools/servicenow/__init__.py +10 -8
  103. alita_sdk/tools/sharepoint/__init__.py +9 -8
  104. alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
  105. alita_sdk/tools/slack/__init__.py +8 -7
  106. alita_sdk/tools/sql/__init__.py +9 -8
  107. alita_sdk/tools/testio/__init__.py +9 -8
  108. alita_sdk/tools/testrail/__init__.py +10 -8
  109. alita_sdk/tools/utils/__init__.py +9 -4
  110. alita_sdk/tools/utils/text_operations.py +254 -0
  111. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +16 -18
  112. alita_sdk/tools/xray/__init__.py +10 -8
  113. alita_sdk/tools/yagmail/__init__.py +8 -3
  114. alita_sdk/tools/zephyr/__init__.py +8 -7
  115. alita_sdk/tools/zephyr_enterprise/__init__.py +10 -8
  116. alita_sdk/tools/zephyr_essential/__init__.py +9 -8
  117. alita_sdk/tools/zephyr_scale/__init__.py +9 -8
  118. alita_sdk/tools/zephyr_squad/__init__.py +9 -8
  119. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/METADATA +1 -1
  120. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/RECORD +124 -119
  121. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/WHEEL +0 -0
  122. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/entry_points.txt +0 -0
  123. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/licenses/LICENSE +0 -0
  124. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, Field, SecretStr
6
6
  from .data_mining_wrapper import AdvancedJiraMiningWrapper
7
7
  from ..base.tool import BaseAction
8
8
  from ..elitea_base import filter_missconfigured_index_tools
9
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ..utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "advanced_jira_mining"
12
12
 
@@ -28,15 +28,13 @@ def get_tools(tool):
28
28
 
29
29
  class AdvancedJiraMiningToolkit(BaseToolkit):
30
30
  tools: List[BaseTool] = []
31
- toolkit_max_length: int = 0
32
31
 
33
32
  @staticmethod
34
33
  def toolkit_config_schema() -> BaseModel:
35
34
  selected_tools = {x['name']: x['args_schema'].schema() for x in AdvancedJiraMiningWrapper.model_construct().get_available_tools()}
36
- AdvancedJiraMiningToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
37
35
  return create_model(
38
36
  name,
39
- jira_base_url=(str, Field(default="", title="Jira URL", description="Jira URL", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AdvancedJiraMiningToolkit.toolkit_max_length})),
37
+ jira_base_url=(str, Field(default="", title="Jira URL", description="Jira URL", json_schema_extra={'toolkit_name': True})),
40
38
  confluence_base_url=(str, Field(default="", title="Confluence URL", description="Confluence URL")),
41
39
  model_type=(str, Field(default="", title="Model type", description="Model type")),
42
40
  summarization_prompt=(Optional[str], Field(default=None, title="Summarization prompt", description="Summarization prompt")),
@@ -66,17 +64,21 @@ class AdvancedJiraMiningToolkit(BaseToolkit):
66
64
  selected_tools = []
67
65
  jira_mining_wrapper = AdvancedJiraMiningWrapper(**kwargs)
68
66
  available_tools = jira_mining_wrapper.get_available_tools()
69
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
70
67
  tools = []
71
68
  for tool in available_tools:
72
69
  if selected_tools:
73
70
  if tool["name"] not in selected_tools:
74
71
  continue
72
+ description = tool["description"]
73
+ if toolkit_name:
74
+ description = f"Toolkit: {toolkit_name}\n{description}"
75
+ description = description[:1000]
75
76
  tools.append(BaseAction(
76
77
  api_wrapper=jira_mining_wrapper,
77
- name=prefix + tool["name"],
78
- description=tool["description"],
79
- args_schema=tool["args_schema"]
78
+ name=tool["name"],
79
+ description=description,
80
+ args_schema=tool["args_schema"],
81
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
80
82
  ))
81
83
  return cls(tools=tools)
82
84
 
@@ -6,7 +6,7 @@ from langchain_core.tools import BaseTool, BaseToolkit
6
6
  from pydantic import BaseModel, Field, computed_field, field_validator
7
7
 
8
8
  from alita_sdk.configurations.delta_lake import DeltaLakeConfiguration
9
- from ...utils import TOOLKIT_SPLITTER, clean_string, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
  from .api_wrapper import DeltaLakeApiWrapper
11
11
  from .tool import DeltaLakeAction
12
12
 
@@ -21,10 +21,6 @@ def get_available_tools() -> dict[str, dict]:
21
21
  }
22
22
  return available_tools
23
23
 
24
- toolkit_max_length = lru_cache(maxsize=1)(
25
- lambda: get_max_toolkit_length(get_available_tools())
26
- )
27
-
28
24
  class DeltaLakeToolkitConfig(BaseModel):
29
25
  class Config:
30
26
  title = name
@@ -87,9 +83,10 @@ class DeltaLakeToolkit(BaseToolkit):
87
83
 
88
84
  @computed_field
89
85
  @property
90
- def tool_prefix(self) -> str:
86
+ def toolkit_context(self) -> str:
87
+ """Returns toolkit context for descriptions (max 1000 chars)."""
91
88
  return (
92
- clean_string(self.toolkit_name, toolkit_max_length()) + TOOLKIT_SPLITTER
89
+ f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
93
90
  if self.toolkit_name
94
91
  else ""
95
92
  )
@@ -118,12 +115,18 @@ class DeltaLakeToolkit(BaseToolkit):
118
115
  selected_tools = set(selected_tools)
119
116
  for t in instance.available_tools:
120
117
  if t["name"] in selected_tools:
118
+ description = t["description"]
119
+ if toolkit_name:
120
+ description = f"Toolkit: {toolkit_name}\n{description}"
121
+ description = f"S3 Path: {getattr(instance.api_wrapper, 's3_path', '')} Table Path: {getattr(instance.api_wrapper, 'table_path', '')}\n{description}"
122
+ description = description[:1000]
121
123
  instance.tools.append(
122
124
  DeltaLakeAction(
123
125
  api_wrapper=instance.api_wrapper,
124
- name=instance.tool_prefix + t["name"],
125
- description=f"S3 Path: {getattr(instance.api_wrapper, 's3_path', '')} Table Path: {getattr(instance.api_wrapper, 'table_path', '')}\n" + t["description"],
126
+ name=t["name"],
127
+ description=description,
126
128
  args_schema=t["args_schema"],
129
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
127
130
  )
128
131
  )
129
132
  return instance
@@ -29,7 +29,11 @@ class DeltaLakeAction(BaseTool):
29
29
  ) -> str:
30
30
  """Use the Delta Lake API to run an operation."""
31
31
  try:
32
+ # Strip numeric suffix added for deduplication (_2, _3, etc.)
33
+ # to get the original tool name that exists in the wrapper
34
+ import re
35
+ tool_name = re.sub(r'_\d+$', '', self.name)
32
36
  # Use the tool name to dispatch to the correct API wrapper method
33
- return self.api_wrapper.run(self.name, *args, **kwargs)
37
+ return self.api_wrapper.run(tool_name, *args, **kwargs)
34
38
  except Exception as e:
35
39
  return f"Error: {format_exc()}"
@@ -6,7 +6,7 @@ from langchain_core.tools import BaseToolkit, BaseTool
6
6
  from pydantic import create_model, BaseModel, ConfigDict, Field
7
7
 
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
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
12
 
@@ -31,12 +31,10 @@ def get_toolkit():
31
31
 
32
32
  class AzureSearchToolkit(BaseToolkit):
33
33
  tools: List[BaseTool] = []
34
- toolkit_max_length: int = 0
35
34
 
36
35
  @staticmethod
37
36
  def toolkit_config_schema() -> BaseModel:
38
37
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureSearchApiWrapper.model_construct().get_available_tools()}
39
- AzureSearchToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
40
38
  m = create_model(
41
39
  name,
42
40
  index_name=(str, Field(description="Azure Search index name")),
@@ -79,17 +77,21 @@ class AzureSearchToolkit(BaseToolkit):
79
77
  }
80
78
  azure_search_api_wrapper = AzureSearchApiWrapper(**wrapper_payload)
81
79
  available_tools = azure_search_api_wrapper.get_available_tools()
82
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
83
80
  tools = []
84
81
  for tool in available_tools:
85
82
  if selected_tools:
86
83
  if tool["name"] not in selected_tools:
87
84
  continue
85
+ description = tool["description"]
86
+ if toolkit_name:
87
+ description = f"Toolkit: {toolkit_name}\n{description}"
88
+ description = description[:1000]
88
89
  tools.append(BaseAction(
89
90
  api_wrapper=azure_search_api_wrapper,
90
- name=prefix + tool["name"],
91
- description=tool["description"],
92
- args_schema=tool["args_schema"]
91
+ name=tool["name"],
92
+ description=description,
93
+ args_schema=tool["args_schema"],
94
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
93
95
  ))
94
96
  return cls(tools=tools)
95
97
 
@@ -23,6 +23,10 @@ class BaseAction(BaseTool):
23
23
  ) -> ToolException | str:
24
24
  """Use the Confluence API to run an operation."""
25
25
  try:
26
- return self.api_wrapper.run(self.name, *args, **kwargs)
26
+ # Strip numeric suffix added for deduplication (_2, _3, etc.)
27
+ # to get the original tool name that exists in the wrapper
28
+ import re
29
+ tool_name = re.sub(r'_\d+$', '', self.name)
30
+ return self.api_wrapper.run(tool_name, *args, **kwargs)
27
31
  except Exception as e:
28
32
  return ToolException(f"An exception occurred: {e}")
@@ -386,7 +386,8 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
386
386
 
387
387
  def remove_index(self, index_name: str = ""):
388
388
  """Cleans the indexed data in the collection."""
389
- super()._clean_collection(index_name=index_name)
389
+ super()._clean_collection(index_name=index_name, including_index_meta=True)
390
+ self._emit_index_data_removed_event(index_name)
390
391
  return (f"Collection '{index_name}' has been removed from the vector store.\n"
391
392
  f"Available collections: {self.list_collections()}") if index_name \
392
393
  else "All collections have been removed from the vector store."
@@ -641,6 +642,30 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
641
642
  except Exception as e:
642
643
  logger.warning(f"Failed to emit index_data_status event: {e}")
643
644
 
645
+ def _emit_index_data_removed_event(self, index_name: str):
646
+ """
647
+ Emit custom event for index data removing.
648
+
649
+ Args:
650
+ index_name: The name of the index
651
+ toolkit_id: The toolkit identifier
652
+ """
653
+ # Build event message
654
+ event_data = {
655
+ "index_name": index_name,
656
+ "toolkit_id": self.toolkit_id,
657
+ "project_id": self.alita.project_id,
658
+ }
659
+ # Emit the event
660
+ try:
661
+ dispatch_custom_event("index_data_removed", event_data)
662
+ logger.debug(
663
+ f"Emitted index_data_removed event for index "
664
+ f"'{index_name}': {event_data}"
665
+ )
666
+ except Exception as e:
667
+ logger.warning(f"Failed to emit index_data_removed event: {e}")
668
+
644
669
  def get_available_tools(self):
645
670
  """
646
671
  Returns the standardized vector search tools (search operations only).
@@ -9,7 +9,7 @@ from pydantic import BaseModel, Field, ConfigDict, create_model
9
9
 
10
10
  from ..base.tool import BaseAction
11
11
  from ..elitea_base import filter_missconfigured_index_tools
12
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
12
+ from ..utils import clean_string, get_max_toolkit_length, check_connection_response
13
13
  from ...configurations.bitbucket import BitbucketConfiguration
14
14
  from ...configurations.pgvector import PgVectorConfiguration
15
15
  import requests
@@ -18,7 +18,7 @@ import requests
18
18
  name = "bitbucket"
19
19
 
20
20
 
21
- def get_tools(tool):
21
+ def get_toolkit(tool):
22
22
  return AlitaBitbucketToolkit.get_toolkit(
23
23
  selected_tools=tool['settings'].get('selected_tools', []),
24
24
  project=tool['settings']['project'],
@@ -33,18 +33,19 @@ def get_tools(tool):
33
33
  doctype='code',
34
34
  embedding_model=tool['settings'].get('embedding_model'),
35
35
  toolkit_name=tool.get('toolkit_name')
36
- ).get_tools()
36
+ )
37
+
38
+ def get_tools(tool):
39
+ return get_toolkit(tool).get_tools()
37
40
 
38
41
 
39
42
  class AlitaBitbucketToolkit(BaseToolkit):
40
43
  tools: List[BaseTool] = []
41
- toolkit_max_length: int = 0
42
44
 
43
45
  @staticmethod
44
46
  def toolkit_config_schema() -> BaseModel:
45
47
  selected_tools = {x['name']: x['args_schema'].schema() for x in
46
48
  BitbucketAPIWrapper.model_construct().get_available_tools()}
47
- AlitaBitbucketToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
48
49
  m = create_model(
49
50
  name,
50
51
  project=(str, Field(description="Project/Workspace")),
@@ -61,7 +62,6 @@ class AlitaBitbucketToolkit(BaseToolkit):
61
62
  'metadata':
62
63
  {
63
64
  "label": "Bitbucket", "icon_url": "bitbucket-icon.svg",
64
- "max_length": AlitaBitbucketToolkit.toolkit_max_length,
65
65
  "categories": ["code repositories"],
66
66
  "extra_categories": ["bitbucket", "git", "repository", "code", "version control"],
67
67
  }
@@ -100,17 +100,21 @@ class AlitaBitbucketToolkit(BaseToolkit):
100
100
  }
101
101
  bitbucket_api_wrapper = BitbucketAPIWrapper(**wrapper_payload)
102
102
  available_tools: List[Dict] = bitbucket_api_wrapper.get_available_tools()
103
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
104
103
  tools = []
105
104
  for tool in available_tools:
106
105
  if selected_tools:
107
106
  if tool['name'] not in selected_tools:
108
107
  continue
108
+ description = tool["description"] + f"\nrepo: {bitbucket_api_wrapper.repository}"
109
+ if toolkit_name:
110
+ description = f"{description}\nToolkit: {toolkit_name}"
111
+ description = description[:1000]
109
112
  tools.append(BaseAction(
110
113
  api_wrapper=bitbucket_api_wrapper,
111
- name=prefix + tool["name"],
112
- description=tool["description"] + f"\nrepo: {bitbucket_api_wrapper.repository}",
113
- args_schema=tool["args_schema"]
114
+ name=tool["name"],
115
+ description=description,
116
+ args_schema=tool["args_schema"],
117
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
114
118
  ))
115
119
  return cls(tools=tools)
116
120
 
@@ -13,6 +13,7 @@ from pydantic.fields import PrivateAttr
13
13
 
14
14
  from ..code_indexer_toolkit import CodeIndexerToolkit
15
15
  from ..utils.available_tools_decorator import extend_with_parent_available_tools
16
+ from ..elitea_base import extend_with_file_operations, BaseCodeToolApiWrapper
16
17
 
17
18
  logger = logging.getLogger(__name__)
18
19
 
@@ -123,6 +124,12 @@ class BitbucketAPIWrapper(CodeIndexerToolkit):
123
124
 
124
125
  _bitbucket: Any = PrivateAttr()
125
126
  _active_branch: Any = PrivateAttr()
127
+
128
+ # Import file operation methods from BaseCodeToolApiWrapper
129
+ read_file_chunk = BaseCodeToolApiWrapper.read_file_chunk
130
+ read_multiple_files = BaseCodeToolApiWrapper.read_multiple_files
131
+ search_file = BaseCodeToolApiWrapper.search_file
132
+ edit_file = BaseCodeToolApiWrapper.edit_file
126
133
  url: str = ''
127
134
  project: str = ''
128
135
  """The key of the project this repo belongs to"""
@@ -360,12 +367,15 @@ class BitbucketAPIWrapper(CodeIndexerToolkit):
360
367
  # except Exception as e:
361
368
  # raise ToolException(f"Can't extract file commit hash (`{file_path}`) due to error:\n{str(e)}")
362
369
 
363
- def _read_file(self, file_path: str, branch: str) -> str:
370
+ def _read_file(self, file_path: str, branch: str, **kwargs) -> str:
364
371
  """
365
- Reads a file from the gitlab repo
372
+ Reads a file from the bitbucket repo with optional partial read support.
373
+
366
374
  Parameters:
367
375
  file_path(str): the file path
368
376
  branch(str): branch name (by default: active_branch)
377
+ **kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
378
+ partial read handled client-side by base class methods
369
379
  Returns:
370
380
  str: The file decoded as a string
371
381
  """
@@ -399,8 +409,46 @@ class BitbucketAPIWrapper(CodeIndexerToolkit):
399
409
  return self._read_file(file_path, branch)
400
410
  except Exception as e:
401
411
  return f"Failed to read file {file_path}: {str(e)}"
412
+
413
+ def _write_file(
414
+ self,
415
+ file_path: str,
416
+ content: str,
417
+ branch: str = None,
418
+ commit_message: str = None
419
+ ) -> str:
420
+ """
421
+ Write content to a file (create or update).
422
+
423
+ Parameters:
424
+ file_path: Path to the file
425
+ content: New file content
426
+ branch: Branch name (uses active branch if None)
427
+ commit_message: Commit message (not used by Bitbucket API)
428
+
429
+ Returns:
430
+ Success message
431
+ """
432
+ try:
433
+ branch = branch or self._active_branch
434
+
435
+ # Check if file exists by attempting to read it
436
+ try:
437
+ self._read_file(file_path, branch)
438
+ # File exists, update it using OLD/NEW format
439
+ old_content = self._read_file(file_path, branch)
440
+ update_query = f"OLD <<<<\n{old_content}\n>>>> OLD\nNEW <<<<\n{content}\n>>>> NEW"
441
+ self._bitbucket.update_file(file_path=file_path, update_query=update_query, branch=branch)
442
+ return f"Updated file {file_path}"
443
+ except:
444
+ # File doesn't exist, create it
445
+ self._bitbucket.create_file(file_path=file_path, file_contents=content, branch=branch)
446
+ return f"Created file {file_path}"
447
+ except Exception as e:
448
+ raise ToolException(f"Unable to write file {file_path}: {str(e)}")
402
449
 
403
450
  @extend_with_parent_available_tools
451
+ @extend_with_file_operations
404
452
  def get_available_tools(self):
405
453
  return [
406
454
  {
@@ -8,7 +8,7 @@ from langchain_community.utilities.wikipedia import WikipediaAPIWrapper
8
8
  from .google_search_rag import GoogleSearchResults
9
9
  from .crawler import SingleURLCrawler, MultiURLCrawler, GetHTMLContent, GetPDFContent
10
10
  from .wiki import WikipediaQueryRun
11
- from ..utils import get_max_toolkit_length, clean_string, TOOLKIT_SPLITTER
11
+ from ..utils import get_max_toolkit_length, clean_string
12
12
  from ...configurations.browser import BrowserConfiguration
13
13
  from logging import getLogger
14
14
 
@@ -42,7 +42,6 @@ class BrowserToolkit(BaseToolkit):
42
42
  'google': GoogleSearchResults.__pydantic_fields__['args_schema'].default.schema(),
43
43
  'wiki': WikipediaQueryRun.__pydantic_fields__['args_schema'].default.schema()
44
44
  }
45
- BrowserToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
46
45
 
47
46
  def validate_google_fields(cls, values):
48
47
  if 'google' in values.get('selected_tools', []):
@@ -90,7 +89,6 @@ class BrowserToolkit(BaseToolkit):
90
89
  }
91
90
 
92
91
  tools = []
93
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
94
92
  if not selected_tools:
95
93
  selected_tools = [
96
94
  'single_url_crawler',
@@ -127,7 +125,10 @@ class BrowserToolkit(BaseToolkit):
127
125
 
128
126
  # Only add the tool if it was successfully created
129
127
  if tool_entry is not None:
130
- tool_entry.name = f"{prefix}{tool_entry.name}"
128
+ if toolkit_name:
129
+ tool_entry.description = f"{tool_entry.description}\nToolkit: {toolkit_name}"
130
+ tool_entry.description = tool_entry.description[:1000]
131
+ tool_entry.metadata = {"toolkit_name": toolkit_name}
131
132
  tools.append(tool_entry)
132
133
  return cls(tools=tools)
133
134
 
@@ -7,7 +7,7 @@ from functools import lru_cache
7
7
  from .api_wrapper import CarrierAPIWrapper
8
8
  from .tools import __all__
9
9
  from ..elitea_base import filter_missconfigured_index_tools
10
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
10
+ from ..utils import clean_string, get_max_toolkit_length
11
11
  from ...configurations.carrier import CarrierConfiguration
12
12
 
13
13
  logger = logging.getLogger(__name__)
@@ -17,7 +17,6 @@ name = 'carrier'
17
17
 
18
18
  class AlitaCarrierToolkit(BaseToolkit):
19
19
  tools: List[BaseTool] = []
20
- toolkit_max_length: int = 100
21
20
 
22
21
  @classmethod
23
22
  @lru_cache(maxsize=32)
@@ -26,7 +25,6 @@ class AlitaCarrierToolkit(BaseToolkit):
26
25
  for t in __all__:
27
26
  default = t['tool'].__pydantic_fields__['args_schema'].default
28
27
  selected_tools[t['name']] = default.schema() if default else default
29
- cls.toolkit_max_length = get_max_toolkit_length(selected_tools)
30
28
  return create_model(
31
29
  name,
32
30
  project_id=(Optional[str], Field(None, description="Optional project ID for scoped operations")),
@@ -70,15 +68,16 @@ class AlitaCarrierToolkit(BaseToolkit):
70
68
  logger.exception(f"[AlitaCarrierToolkit] Error initializing CarrierAPIWrapper: {e}")
71
69
  raise ValueError(f"CarrierAPIWrapper initialization error: {e}")
72
70
 
73
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
74
-
75
71
  tools = []
76
72
  for tool_def in __all__:
77
73
  if selected_tools and tool_def['name'] not in selected_tools:
78
74
  continue
79
75
  try:
80
76
  tool_instance = tool_def['tool'](api_wrapper=carrier_api_wrapper)
81
- tool_instance.name = prefix + tool_instance.name
77
+ if toolkit_name:
78
+ tool_instance.description = f"{tool_instance.description}\nToolkit: {toolkit_name}"
79
+ tool_instance.description = tool_instance.description[:1000]
80
+ tool_instance.metadata = {"toolkit_name": toolkit_name}
82
81
  tools.append(tool_instance)
83
82
  logger.info(f"[AlitaCarrierToolkit] Successfully initialized tool '{tool_instance.name}'")
84
83
  except Exception as e:
@@ -17,6 +17,7 @@ def json_chunker(file_content_generator: Generator[Document, None, None], config
17
17
  for chunk in chunks:
18
18
  metadata = doc.metadata.copy()
19
19
  metadata['chunk_id'] = chunk_id
20
+ metadata['method_name'] = 'json'
20
21
  chunk_id += 1
21
22
  yield Document(page_content=json.dumps(chunk), metadata=metadata)
22
23
  except Exception as e:
@@ -60,6 +60,7 @@ def markdown_chunker(file_content_generator: Generator[Document, None, None], co
60
60
  docmeta.update({"headers": "; ".join(headers_meta)})
61
61
  docmeta['chunk_id'] = chunk_id
62
62
  docmeta['chunk_type'] = "document"
63
+ docmeta['method_name'] = 'markdown'
63
64
  yield Document(
64
65
  page_content=subchunk,
65
66
  metadata=docmeta
@@ -71,6 +72,7 @@ def markdown_chunker(file_content_generator: Generator[Document, None, None], co
71
72
  docmeta.update({"headers": "; ".join(headers_meta)})
72
73
  docmeta['chunk_id'] = chunk_id
73
74
  docmeta['chunk_type'] = "document"
75
+ docmeta['method_name'] = 'text'
74
76
  yield Document(
75
77
  page_content=chunk.page_content,
76
78
  metadata=docmeta
@@ -86,6 +86,7 @@ def _default_text_chunker(
86
86
  for idx, chunk in enumerate(chunks, 1):
87
87
  chunk.metadata['chunk_id'] = idx
88
88
  chunk.metadata['chunk_type'] = 'text'
89
+ chunk.metadata['method_name'] = 'text'
89
90
  yield chunk
90
91
 
91
92
 
@@ -6,7 +6,7 @@ from langchain_core.tools import BaseToolkit, BaseTool
6
6
  from .api_wrapper import AWSToolConfig
7
7
  from ...base.tool import BaseAction
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "aws"
12
12
 
@@ -22,12 +22,10 @@ def get_tools(tool):
22
22
 
23
23
  class AWSToolkit(BaseToolkit):
24
24
  tools: list[BaseTool] = []
25
- toolkit_max_length: int = 0
26
25
 
27
26
  @staticmethod
28
27
  def toolkit_config_schema() -> BaseModel:
29
28
  selected_tools = {x['name']: x['args_schema'].schema() for x in AWSToolConfig.model_construct().get_available_tools()}
30
- AWSToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
31
29
  return create_model(
32
30
  name,
33
31
  region=(str, Field(default="", title="Region", description="AWS region")),
@@ -54,15 +52,19 @@ class AWSToolkit(BaseToolkit):
54
52
  aws_tool_config = AWSToolConfig(**kwargs)
55
53
  available_tools = aws_tool_config.get_available_tools()
56
54
  tools = []
57
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
58
55
  for tool in available_tools:
59
56
  if selected_tools and tool["name"] not in selected_tools:
60
57
  continue
58
+ description = tool["description"]
59
+ if toolkit_name:
60
+ description = f"Toolkit: {toolkit_name}\n{description}"
61
+ description = description[:1000]
61
62
  tools.append(BaseAction(
62
63
  api_wrapper=aws_tool_config,
63
- name=prefix + tool["name"],
64
- description=tool["description"],
65
- args_schema=tool["args_schema"]
64
+ name=tool["name"],
65
+ description=description,
66
+ args_schema=tool["args_schema"],
67
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
66
68
  ))
67
69
  return cls(tools=tools)
68
70
 
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
6
6
  from .api_wrapper import AzureApiWrapper
7
7
  from ...base.tool import BaseAction
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "azure"
12
12
 
@@ -23,12 +23,10 @@ def get_tools(tool):
23
23
 
24
24
  class AzureToolkit(BaseToolkit):
25
25
  tools: list[BaseTool] = []
26
- toolkit_max_length: int = 0
27
26
 
28
27
  @staticmethod
29
28
  def toolkit_config_schema() -> BaseModel:
30
29
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureApiWrapper.model_construct().get_available_tools()}
31
- AzureToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
32
30
  return create_model(
33
31
  name,
34
32
  subscription_id=(str, Field(default="", title="Subscription ID", description="Azure subscription ID")),
@@ -47,15 +45,19 @@ class AzureToolkit(BaseToolkit):
47
45
  azure_api_wrapper = AzureApiWrapper(**kwargs)
48
46
  available_tools = azure_api_wrapper.get_available_tools()
49
47
  tools = []
50
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
51
48
  for tool in available_tools:
52
49
  if selected_tools and tool["name"] not in selected_tools:
53
50
  continue
51
+ description = tool["description"]
52
+ if toolkit_name:
53
+ description = f"Toolkit: {toolkit_name}\n{description}"
54
+ description = description[:1000]
54
55
  tools.append(BaseAction(
55
56
  api_wrapper=azure_api_wrapper,
56
- name=prefix + tool["name"],
57
- description=tool["description"],
58
- args_schema=tool["args_schema"]
57
+ name=tool["name"],
58
+ description=description,
59
+ args_schema=tool["args_schema"],
60
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
59
61
  ))
60
62
  return cls(tools=tools)
61
63
 
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
6
6
  from .api_wrapper import GCPApiWrapper
7
7
  from ...base.tool import BaseAction
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "gcp"
12
12
 
@@ -20,12 +20,10 @@ def get_tools(tool):
20
20
 
21
21
  class GCPToolkit(BaseToolkit):
22
22
  tools: list[BaseTool] = []
23
- toolkit_max_length: int = 0
24
23
 
25
24
  @staticmethod
26
25
  def toolkit_config_schema() -> BaseModel:
27
26
  selected_tools = {x['name']: x['args_schema'].schema() for x in GCPApiWrapper.model_construct().get_available_tools()}
28
- GCPToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
29
27
  return create_model(
30
28
  name,
31
29
  api_key=(SecretStr, Field(default="", title="API key", description="GCP API key", json_schema_extra={'secret': True})),
@@ -41,15 +39,19 @@ class GCPToolkit(BaseToolkit):
41
39
  gcp_api_wrapper = GCPApiWrapper(**kwargs)
42
40
  available_tools = gcp_api_wrapper.get_available_tools()
43
41
  tools = []
44
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
45
42
  for tool in available_tools:
46
43
  if selected_tools and tool["name"] not in selected_tools:
47
44
  continue
45
+ description = tool["description"]
46
+ if toolkit_name:
47
+ description = f"Toolkit: {toolkit_name}\n{description}"
48
+ description = description[:1000]
48
49
  tools.append(BaseAction(
49
50
  api_wrapper=gcp_api_wrapper,
50
- name=prefix + tool["name"],
51
- description=tool["description"],
52
- args_schema=tool["args_schema"]
51
+ name=tool["name"],
52
+ description=description,
53
+ args_schema=tool["args_schema"],
54
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
53
55
  ))
54
56
  return cls(tools=tools)
55
57