alita-sdk 0.3.462__py3-none-any.whl → 0.3.627__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. alita_sdk/cli/agent/__init__.py +5 -0
  2. alita_sdk/cli/agent/default.py +258 -0
  3. alita_sdk/cli/agent_executor.py +15 -3
  4. alita_sdk/cli/agent_loader.py +56 -8
  5. alita_sdk/cli/agent_ui.py +93 -31
  6. alita_sdk/cli/agents.py +2274 -230
  7. alita_sdk/cli/callbacks.py +96 -25
  8. alita_sdk/cli/cli.py +10 -1
  9. alita_sdk/cli/config.py +162 -9
  10. alita_sdk/cli/context/__init__.py +30 -0
  11. alita_sdk/cli/context/cleanup.py +198 -0
  12. alita_sdk/cli/context/manager.py +731 -0
  13. alita_sdk/cli/context/message.py +285 -0
  14. alita_sdk/cli/context/strategies.py +289 -0
  15. alita_sdk/cli/context/token_estimation.py +127 -0
  16. alita_sdk/cli/input_handler.py +419 -0
  17. alita_sdk/cli/inventory.py +1073 -0
  18. alita_sdk/cli/testcases/__init__.py +94 -0
  19. alita_sdk/cli/testcases/data_generation.py +119 -0
  20. alita_sdk/cli/testcases/discovery.py +96 -0
  21. alita_sdk/cli/testcases/executor.py +84 -0
  22. alita_sdk/cli/testcases/logger.py +85 -0
  23. alita_sdk/cli/testcases/parser.py +172 -0
  24. alita_sdk/cli/testcases/prompts.py +91 -0
  25. alita_sdk/cli/testcases/reporting.py +125 -0
  26. alita_sdk/cli/testcases/setup.py +108 -0
  27. alita_sdk/cli/testcases/test_runner.py +282 -0
  28. alita_sdk/cli/testcases/utils.py +39 -0
  29. alita_sdk/cli/testcases/validation.py +90 -0
  30. alita_sdk/cli/testcases/workflow.py +196 -0
  31. alita_sdk/cli/toolkit.py +14 -17
  32. alita_sdk/cli/toolkit_loader.py +35 -5
  33. alita_sdk/cli/tools/__init__.py +36 -2
  34. alita_sdk/cli/tools/approval.py +224 -0
  35. alita_sdk/cli/tools/filesystem.py +910 -64
  36. alita_sdk/cli/tools/planning.py +389 -0
  37. alita_sdk/cli/tools/terminal.py +414 -0
  38. alita_sdk/community/__init__.py +72 -12
  39. alita_sdk/community/inventory/__init__.py +236 -0
  40. alita_sdk/community/inventory/config.py +257 -0
  41. alita_sdk/community/inventory/enrichment.py +2137 -0
  42. alita_sdk/community/inventory/extractors.py +1469 -0
  43. alita_sdk/community/inventory/ingestion.py +3172 -0
  44. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  45. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  46. alita_sdk/community/inventory/parsers/base.py +295 -0
  47. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  48. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  49. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  50. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  51. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  52. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  53. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  54. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  55. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  56. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  57. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  58. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  59. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  60. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  61. alita_sdk/community/inventory/patterns/loader.py +348 -0
  62. alita_sdk/community/inventory/patterns/registry.py +198 -0
  63. alita_sdk/community/inventory/presets.py +535 -0
  64. alita_sdk/community/inventory/retrieval.py +1403 -0
  65. alita_sdk/community/inventory/toolkit.py +173 -0
  66. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  67. alita_sdk/community/inventory/visualize.py +1370 -0
  68. alita_sdk/configurations/__init__.py +1 -1
  69. alita_sdk/configurations/ado.py +141 -20
  70. alita_sdk/configurations/bitbucket.py +0 -3
  71. alita_sdk/configurations/confluence.py +76 -42
  72. alita_sdk/configurations/figma.py +76 -0
  73. alita_sdk/configurations/gitlab.py +17 -5
  74. alita_sdk/configurations/openapi.py +329 -0
  75. alita_sdk/configurations/qtest.py +72 -1
  76. alita_sdk/configurations/report_portal.py +96 -0
  77. alita_sdk/configurations/sharepoint.py +148 -0
  78. alita_sdk/configurations/testio.py +83 -0
  79. alita_sdk/runtime/clients/artifact.py +3 -3
  80. alita_sdk/runtime/clients/client.py +353 -48
  81. alita_sdk/runtime/clients/sandbox_client.py +0 -21
  82. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  83. alita_sdk/runtime/langchain/assistant.py +123 -26
  84. alita_sdk/runtime/langchain/constants.py +642 -1
  85. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  86. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  87. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +6 -3
  88. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  89. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  90. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  91. alita_sdk/runtime/langchain/langraph_agent.py +279 -73
  92. alita_sdk/runtime/langchain/utils.py +82 -15
  93. alita_sdk/runtime/llms/preloaded.py +2 -6
  94. alita_sdk/runtime/skills/__init__.py +91 -0
  95. alita_sdk/runtime/skills/callbacks.py +498 -0
  96. alita_sdk/runtime/skills/discovery.py +540 -0
  97. alita_sdk/runtime/skills/executor.py +610 -0
  98. alita_sdk/runtime/skills/input_builder.py +371 -0
  99. alita_sdk/runtime/skills/models.py +330 -0
  100. alita_sdk/runtime/skills/registry.py +355 -0
  101. alita_sdk/runtime/skills/skill_runner.py +330 -0
  102. alita_sdk/runtime/toolkits/__init__.py +7 -0
  103. alita_sdk/runtime/toolkits/application.py +21 -9
  104. alita_sdk/runtime/toolkits/artifact.py +15 -5
  105. alita_sdk/runtime/toolkits/datasource.py +13 -6
  106. alita_sdk/runtime/toolkits/mcp.py +139 -251
  107. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  108. alita_sdk/runtime/toolkits/planning.py +178 -0
  109. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  110. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  111. alita_sdk/runtime/toolkits/tools.py +238 -32
  112. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  113. alita_sdk/runtime/tools/__init__.py +3 -1
  114. alita_sdk/runtime/tools/application.py +20 -6
  115. alita_sdk/runtime/tools/artifact.py +511 -28
  116. alita_sdk/runtime/tools/data_analysis.py +183 -0
  117. alita_sdk/runtime/tools/function.py +43 -15
  118. alita_sdk/runtime/tools/image_generation.py +50 -44
  119. alita_sdk/runtime/tools/llm.py +852 -67
  120. alita_sdk/runtime/tools/loop.py +3 -1
  121. alita_sdk/runtime/tools/loop_output.py +3 -1
  122. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  123. alita_sdk/runtime/tools/mcp_server_tool.py +7 -6
  124. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  125. alita_sdk/runtime/tools/planning/models.py +246 -0
  126. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  127. alita_sdk/runtime/tools/router.py +2 -4
  128. alita_sdk/runtime/tools/sandbox.py +9 -6
  129. alita_sdk/runtime/tools/skill_router.py +776 -0
  130. alita_sdk/runtime/tools/tool.py +3 -1
  131. alita_sdk/runtime/tools/vectorstore.py +7 -2
  132. alita_sdk/runtime/tools/vectorstore_base.py +51 -11
  133. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  134. alita_sdk/runtime/utils/constants.py +5 -1
  135. alita_sdk/runtime/utils/mcp_client.py +492 -0
  136. alita_sdk/runtime/utils/mcp_oauth.py +202 -5
  137. alita_sdk/runtime/utils/mcp_sse_client.py +36 -7
  138. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  139. alita_sdk/runtime/utils/serialization.py +155 -0
  140. alita_sdk/runtime/utils/streamlit.py +6 -10
  141. alita_sdk/runtime/utils/toolkit_utils.py +16 -5
  142. alita_sdk/runtime/utils/utils.py +36 -0
  143. alita_sdk/tools/__init__.py +113 -29
  144. alita_sdk/tools/ado/repos/__init__.py +51 -33
  145. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  146. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  147. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  148. alita_sdk/tools/ado/utils.py +1 -18
  149. alita_sdk/tools/ado/wiki/__init__.py +25 -8
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  151. alita_sdk/tools/ado/work_item/__init__.py +26 -9
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  155. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  156. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  157. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  158. alita_sdk/tools/base/tool.py +5 -1
  159. alita_sdk/tools/base_indexer_toolkit.py +170 -45
  160. alita_sdk/tools/bitbucket/__init__.py +17 -12
  161. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  162. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  163. alita_sdk/tools/browser/__init__.py +5 -4
  164. alita_sdk/tools/carrier/__init__.py +5 -6
  165. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  166. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  167. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  168. alita_sdk/tools/chunkers/__init__.py +3 -1
  169. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  170. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  171. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  172. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  173. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  174. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  175. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  176. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  177. alita_sdk/tools/code/linter/__init__.py +10 -8
  178. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  179. alita_sdk/tools/code/sonar/__init__.py +10 -7
  180. alita_sdk/tools/code_indexer_toolkit.py +73 -23
  181. alita_sdk/tools/confluence/__init__.py +21 -15
  182. alita_sdk/tools/confluence/api_wrapper.py +78 -23
  183. alita_sdk/tools/confluence/loader.py +4 -2
  184. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  185. alita_sdk/tools/elastic/__init__.py +11 -8
  186. alita_sdk/tools/elitea_base.py +493 -30
  187. alita_sdk/tools/figma/__init__.py +58 -11
  188. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  189. alita_sdk/tools/figma/figma_client.py +73 -0
  190. alita_sdk/tools/figma/toon_tools.py +2748 -0
  191. alita_sdk/tools/github/__init__.py +13 -14
  192. alita_sdk/tools/github/github_client.py +224 -100
  193. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  194. alita_sdk/tools/github/schemas.py +14 -5
  195. alita_sdk/tools/github/tool.py +5 -1
  196. alita_sdk/tools/github/tool_prompts.py +9 -22
  197. alita_sdk/tools/gitlab/__init__.py +15 -11
  198. alita_sdk/tools/gitlab/api_wrapper.py +207 -41
  199. alita_sdk/tools/gitlab_org/__init__.py +10 -8
  200. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  201. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  202. alita_sdk/tools/google/bigquery/tool.py +5 -1
  203. alita_sdk/tools/google_places/__init__.py +10 -8
  204. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  205. alita_sdk/tools/jira/__init__.py +17 -11
  206. alita_sdk/tools/jira/api_wrapper.py +91 -40
  207. alita_sdk/tools/keycloak/__init__.py +11 -8
  208. alita_sdk/tools/localgit/__init__.py +9 -3
  209. alita_sdk/tools/localgit/local_git.py +62 -54
  210. alita_sdk/tools/localgit/tool.py +5 -1
  211. alita_sdk/tools/memory/__init__.py +11 -3
  212. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  213. alita_sdk/tools/ocr/__init__.py +11 -8
  214. alita_sdk/tools/openapi/__init__.py +490 -114
  215. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  216. alita_sdk/tools/openapi/tool.py +20 -0
  217. alita_sdk/tools/pandas/__init__.py +20 -12
  218. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  219. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  220. alita_sdk/tools/postman/__init__.py +11 -11
  221. alita_sdk/tools/pptx/__init__.py +10 -9
  222. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  223. alita_sdk/tools/qtest/__init__.py +30 -10
  224. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  225. alita_sdk/tools/rally/__init__.py +10 -8
  226. alita_sdk/tools/rally/api_wrapper.py +1 -1
  227. alita_sdk/tools/report_portal/__init__.py +12 -9
  228. alita_sdk/tools/salesforce/__init__.py +10 -9
  229. alita_sdk/tools/servicenow/__init__.py +17 -14
  230. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  231. alita_sdk/tools/sharepoint/__init__.py +10 -8
  232. alita_sdk/tools/sharepoint/api_wrapper.py +4 -4
  233. alita_sdk/tools/slack/__init__.py +10 -8
  234. alita_sdk/tools/slack/api_wrapper.py +2 -2
  235. alita_sdk/tools/sql/__init__.py +11 -9
  236. alita_sdk/tools/testio/__init__.py +10 -8
  237. alita_sdk/tools/testrail/__init__.py +11 -8
  238. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  239. alita_sdk/tools/utils/__init__.py +9 -4
  240. alita_sdk/tools/utils/content_parser.py +77 -3
  241. alita_sdk/tools/utils/text_operations.py +410 -0
  242. alita_sdk/tools/utils/tool_prompts.py +79 -0
  243. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +17 -13
  244. alita_sdk/tools/xray/__init__.py +12 -9
  245. alita_sdk/tools/yagmail/__init__.py +9 -3
  246. alita_sdk/tools/zephyr/__init__.py +9 -7
  247. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -8
  248. alita_sdk/tools/zephyr_essential/__init__.py +10 -8
  249. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  250. alita_sdk/tools/zephyr_essential/client.py +2 -2
  251. alita_sdk/tools/zephyr_scale/__init__.py +11 -9
  252. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  253. alita_sdk/tools/zephyr_squad/__init__.py +10 -8
  254. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +147 -7
  255. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  256. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  257. alita_sdk-0.3.462.dist-info/RECORD +0 -384
  258. alita_sdk-0.3.462.dist-info/entry_points.txt +0 -2
  259. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  260. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  261. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -6,9 +6,10 @@ 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
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
12
13
 
13
14
  name = "delta_lake"
14
15
 
@@ -21,10 +22,6 @@ def get_available_tools() -> dict[str, dict]:
21
22
  }
22
23
  return available_tools
23
24
 
24
- toolkit_max_length = lru_cache(maxsize=1)(
25
- lambda: get_max_toolkit_length(get_available_tools())
26
- )
27
-
28
25
  class DeltaLakeToolkitConfig(BaseModel):
29
26
  class Config:
30
27
  title = name
@@ -87,9 +84,10 @@ class DeltaLakeToolkit(BaseToolkit):
87
84
 
88
85
  @computed_field
89
86
  @property
90
- def tool_prefix(self) -> str:
87
+ def toolkit_context(self) -> str:
88
+ """Returns toolkit context for descriptions (max 1000 chars)."""
91
89
  return (
92
- clean_string(self.toolkit_name, toolkit_max_length()) + TOOLKIT_SPLITTER
90
+ f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
93
91
  if self.toolkit_name
94
92
  else ""
95
93
  )
@@ -118,12 +116,18 @@ class DeltaLakeToolkit(BaseToolkit):
118
116
  selected_tools = set(selected_tools)
119
117
  for t in instance.available_tools:
120
118
  if t["name"] in selected_tools:
119
+ description = t["description"]
120
+ if toolkit_name:
121
+ description = f"Toolkit: {toolkit_name}\n{description}"
122
+ description = f"S3 Path: {getattr(instance.api_wrapper, 's3_path', '')} Table Path: {getattr(instance.api_wrapper, 'table_path', '')}\n{description}"
123
+ description = description[:1000]
121
124
  instance.tools.append(
122
125
  DeltaLakeAction(
123
126
  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"],
127
+ name=t["name"],
128
+ description=description,
126
129
  args_schema=t["args_schema"],
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"]}
127
131
  )
128
132
  )
129
133
  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,9 +6,10 @@ 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
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
12
13
 
13
14
  logger = getLogger(__name__)
14
15
 
@@ -31,12 +32,10 @@ def get_toolkit():
31
32
 
32
33
  class AzureSearchToolkit(BaseToolkit):
33
34
  tools: List[BaseTool] = []
34
- toolkit_max_length: int = 0
35
35
 
36
36
  @staticmethod
37
37
  def toolkit_config_schema() -> BaseModel:
38
38
  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
39
  m = create_model(
41
40
  name,
42
41
  index_name=(str, Field(description="Azure Search index name")),
@@ -46,7 +45,7 @@ class AzureSearchToolkit(BaseToolkit):
46
45
  ),
47
46
  api_version=(Optional[str], Field(description="API version", default=None)),
48
47
  openai_api_key=(Optional[str], Field(description="Azure OpenAI API Key", default=None, json_schema_extra={'secret': True})),
49
- model_name=(str, Field(description="Model name for Embeddings model", default=None)),
48
+ model_name=(Optional[str], Field(description="Model name for Embeddings model", default=None)),
50
49
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
51
50
  __config__=ConfigDict(json_schema_extra={
52
51
  'metadata': {
@@ -79,17 +78,21 @@ class AzureSearchToolkit(BaseToolkit):
79
78
  }
80
79
  azure_search_api_wrapper = AzureSearchApiWrapper(**wrapper_payload)
81
80
  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
81
  tools = []
84
82
  for tool in available_tools:
85
83
  if selected_tools:
86
84
  if tool["name"] not in selected_tools:
87
85
  continue
86
+ description = tool["description"]
87
+ if toolkit_name:
88
+ description = f"Toolkit: {toolkit_name}\n{description}"
89
+ description = description[:1000]
88
90
  tools.append(BaseAction(
89
91
  api_wrapper=azure_search_api_wrapper,
90
- name=prefix + tool["name"],
91
- description=tool["description"],
92
- args_schema=tool["args_schema"]
92
+ name=tool["name"],
93
+ description=description,
94
+ args_schema=tool["args_schema"],
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"]}
93
96
  ))
94
97
  return cls(tools=tools)
95
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):
@@ -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}")
@@ -2,6 +2,7 @@ import copy
2
2
  import json
3
3
  import logging
4
4
  import time
5
+ from enum import Enum
5
6
  from typing import Any, Optional, List, Dict, Generator
6
7
 
7
8
  from langchain_core.callbacks import dispatch_custom_event
@@ -16,13 +17,17 @@ from ..runtime.utils.utils import IndexerKeywords
16
17
 
17
18
  logger = logging.getLogger(__name__)
18
19
 
19
- DEFAULT_CUT_OFF = 0.2
20
+ DEFAULT_CUT_OFF = 0.1
21
+ INDEX_META_UPDATE_INTERVAL = 600.0
20
22
 
21
- # Base Vector Store Schema Models
22
- BaseIndexParams = create_model(
23
- "BaseIndexParams",
24
- index_name=(str, Field(description="Index name (max 7 characters)", min_length=1, max_length=7)),
25
- )
23
+ class IndexTools(str, Enum):
24
+ """Enum for index-related tool names."""
25
+ INDEX_DATA = "index_data"
26
+ SEARCH_INDEX = "search_index"
27
+ STEPBACK_SEARCH_INDEX = "stepback_search_index"
28
+ STEPBACK_SUMMARY_INDEX = "stepback_summary_index"
29
+ REMOVE_INDEX = "remove_index"
30
+ LIST_COLLECTIONS = "list_collections"
26
31
 
27
32
  RemoveIndexParams = create_model(
28
33
  "RemoveIndexParams",
@@ -41,7 +46,7 @@ BaseSearchParams = create_model(
41
46
  examples=["{\"key\": \"value\"}", "{\"status\": \"active\"}"]
42
47
  )),
43
48
  cut_off=(Optional[float], Field(description="Cut-off score for search results", default=DEFAULT_CUT_OFF, ge=0, le=1)),
44
- 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)),
45
50
  full_text_search=(Optional[Dict[str, Any]], Field(
46
51
  description="Full text search parameters. Can be a dictionary with search options.",
47
52
  default=None
@@ -71,7 +76,7 @@ BaseStepbackSearchParams = create_model(
71
76
  examples=["{\"key\": \"value\"}", "{\"status\": \"active\"}"]
72
77
  )),
73
78
  cut_off=(Optional[float], Field(description="Cut-off score for search results", default=DEFAULT_CUT_OFF, ge=0, le=1)),
74
- 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)),
75
80
  full_text_search=(Optional[Dict[str, Any]], Field(
76
81
  description="Full text search parameters. Can be a dictionary with search options.",
77
82
  default=None
@@ -90,16 +95,6 @@ BaseStepbackSearchParams = create_model(
90
95
  )),
91
96
  )
92
97
 
93
- BaseIndexDataParams = create_model(
94
- "indexData",
95
- __base__=BaseIndexParams,
96
- clean_index=(Optional[bool], Field(default=False,
97
- description="Optional flag to enforce clean existing index before indexing new data")),
98
- progress_step=(Optional[int], Field(default=10, ge=0, le=100,
99
- description="Optional step size for progress reporting during indexing")),
100
- chunking_config=(Optional[dict], Field(description="Chunking tool configuration", default=loaders_allowed_to_override)),
101
- )
102
-
103
98
 
104
99
  class BaseIndexerToolkit(VectorStoreWrapperBase):
105
100
  """Base class for tool API wrappers that support vector store functionality."""
@@ -157,6 +152,16 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
157
152
  clean_index = kwargs.get("clean_index")
158
153
  chunking_tool = kwargs.get("chunking_tool")
159
154
  chunking_config = kwargs.get("chunking_config")
155
+
156
+ # Store the interval in a private dict to avoid Pydantic field errors
157
+ if not hasattr(self, "_index_meta_config"):
158
+ self._index_meta_config: Dict[str, Any] = {}
159
+
160
+ self._index_meta_config["update_interval"] = kwargs.get(
161
+ "meta_update_interval",
162
+ INDEX_META_UPDATE_INTERVAL,
163
+ )
164
+
160
165
  result = {"count": 0}
161
166
  #
162
167
  try:
@@ -164,6 +169,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
164
169
  self._clean_index(index_name)
165
170
  #
166
171
  self.index_meta_init(index_name, kwargs)
172
+ self._emit_index_event(index_name)
167
173
  #
168
174
  self._log_tool_event(f"Indexing data into collection with suffix '{index_name}'. It can take some time...")
169
175
  self._log_tool_event(f"Loading the documents to index...{kwargs}")
@@ -179,18 +185,26 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
179
185
  self._save_index_generator(documents, documents_count, chunking_tool, chunking_config, index_name=index_name, result=result)
180
186
  #
181
187
  results_count = result["count"]
182
- self.index_meta_update(index_name, IndexerKeywords.INDEX_META_COMPLETED.value, results_count)
188
+ # Final update should always be forced
189
+ self.index_meta_update(index_name, IndexerKeywords.INDEX_META_COMPLETED.value, results_count, update_force=True, error=None)
183
190
  self._emit_index_event(index_name)
184
191
  #
185
192
  return {"status": "ok", "message": f"successfully indexed {results_count} documents" if results_count > 0
186
193
  else "no new documents to index"}
187
194
  except Exception as e:
188
- self.index_meta_update(index_name, IndexerKeywords.INDEX_META_FAILED.value, result["count"])
189
- self._emit_index_event(index_name, error=str(e))
195
+ # Do maximum effort at least send custom event for supposed changed status
196
+ msg = str(e)
197
+ try:
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
+ except Exception as ie:
201
+ logger.error(f"Failed to update index meta status to FAILED for index '{index_name}': {ie}")
202
+ msg = f"{msg}; additionally failed to update index meta status to FAILED: {ie}"
203
+ self._emit_index_event(index_name, error=msg)
190
204
  raise e
191
-
192
205
 
193
206
  def _save_index_generator(self, base_documents: Generator[Document, None, None], base_total: int, chunking_tool, chunking_config, result, index_name: Optional[str] = None):
207
+ self._ensure_vectorstore_initialized()
194
208
  self._log_tool_event(f"Base documents are ready for indexing. {base_total} base documents in total to index.")
195
209
  from ..runtime.langchain.interfaces.llm_processor import add_documents
196
210
  #
@@ -206,7 +220,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
206
220
  self._log_tool_event(f"Dependent documents were processed. "
207
221
  f"Applying chunking tool '{chunking_tool}' if specified and preparing documents for indexing...")
208
222
  documents = self._apply_loaders_chunkers(documents, chunking_tool, chunking_config)
209
- self._clean_metadata(documents)
223
+ documents = self._clean_metadata(documents)
210
224
 
211
225
  logger.debug(f"Indexing base document #{base_doc_counter}: {base_doc} and all dependent documents: {documents}")
212
226
 
@@ -243,6 +257,11 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
243
257
  logger.debug(msg)
244
258
  self._log_tool_event(msg)
245
259
  result["count"] += dependent_docs_counter
260
+ # After each base document, try a non-forced meta update; throttling handled inside index_meta_update
261
+ try:
262
+ self.index_meta_update(index_name, IndexerKeywords.INDEX_META_IN_PROGRESS.value, result["count"], update_force=False)
263
+ except Exception as exc: # best-effort, do not break indexing
264
+ logger.warning(f"Failed to update index meta during indexing process for index '{index_name}': {exc}")
246
265
  if pg_vector_add_docs_chunk:
247
266
  add_documents(vectorstore=self.vectorstore, documents=pg_vector_add_docs_chunk)
248
267
 
@@ -308,6 +327,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
308
327
  log_msg: str = "Verification of documents to index started"
309
328
  ) -> Generator[Document, None, None]:
310
329
  """Generic duplicate reduction logic for documents."""
330
+ self._ensure_vectorstore_initialized()
311
331
  self._log_tool_event(log_msg, tool_name="index_documents")
312
332
  indexed_data = self._get_indexed_data(index_name)
313
333
  indexed_keys = set(indexed_data.keys())
@@ -350,7 +370,8 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
350
370
 
351
371
  def remove_index(self, index_name: str = ""):
352
372
  """Cleans the indexed data in the collection."""
353
- super()._clean_collection(index_name=index_name)
373
+ super()._clean_collection(index_name=index_name, including_index_meta=True)
374
+ self._emit_index_data_removed_event(index_name)
354
375
  return (f"Collection '{index_name}' has been removed from the vector store.\n"
355
376
  f"Available collections: {self.list_collections()}") if index_name \
356
377
  else "All collections have been removed from the vector store."
@@ -463,6 +484,7 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
463
484
  )
464
485
 
465
486
  def index_meta_init(self, index_name: str, index_configuration: dict[str, Any]):
487
+ self._ensure_vectorstore_initialized()
466
488
  index_meta = super().get_index_meta(index_name)
467
489
  if not index_meta:
468
490
  self._log_tool_event(
@@ -482,12 +504,56 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
482
504
  "updated_on": created_on,
483
505
  "task_id": None,
484
506
  "conversation_id": None,
507
+ "toolkit_id": self.toolkit_id,
508
+ # Initialize error field to keep track of the latest failure reason if any
509
+ "error": None,
485
510
  }
486
511
  metadata["history"] = json.dumps([metadata])
487
512
  index_meta_doc = Document(page_content=f"{IndexerKeywords.INDEX_META_TYPE.value}_{index_name}", metadata=metadata)
488
513
  add_documents(vectorstore=self.vectorstore, documents=[index_meta_doc])
489
514
 
490
- def index_meta_update(self, index_name: str, state: str, result: int):
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):
516
+ """Update `index_meta` document with optional time-based throttling.
517
+
518
+ Args:
519
+ index_name: Index name to update meta for.
520
+ state: New state value for the `index_meta` record.
521
+ result: Number of processed documents to store in the `updated` field.
522
+ update_force: If `True`, perform the update unconditionally, ignoring throttling.
523
+ If `False`, perform the update only when the effective time interval has passed.
524
+ interval: Optional custom interval (in seconds) for this call when `update_force` is `False`.
525
+ If `None`, falls back to the value stored in `self._index_meta_config["update_interval"]`
526
+ if present, otherwise uses `INDEX_META_UPDATE_INTERVAL`.
527
+ error: Optional error message to record when the state represents a failed index.
528
+ """
529
+ self._ensure_vectorstore_initialized()
530
+ if not hasattr(self, "_index_meta_last_update_time"):
531
+ self._index_meta_last_update_time: Dict[str, float] = {}
532
+
533
+ if not update_force:
534
+ # Resolve effective interval:
535
+ # 1\) explicit arg
536
+ # 2\) value from `_index_meta_config`
537
+ # 3\) default constant
538
+ cfg_interval = None
539
+ if hasattr(self, "_index_meta_config"):
540
+ cfg_interval = self._index_meta_config.get("update_interval")
541
+
542
+ eff_interval = (
543
+ interval
544
+ if interval is not None
545
+ else (cfg_interval if cfg_interval is not None else INDEX_META_UPDATE_INTERVAL)
546
+ )
547
+
548
+ last_time = self._index_meta_last_update_time.get(index_name)
549
+ now = time.time()
550
+ if last_time is not None and (now - last_time) < eff_interval:
551
+ return
552
+ self._index_meta_last_update_time[index_name] = now
553
+ else:
554
+ # For forced updates, always refresh last update time
555
+ self._index_meta_last_update_time[index_name] = time.time()
556
+
491
557
  index_meta_raw = super().get_index_meta(index_name)
492
558
  from ..runtime.langchain.interfaces.llm_processor import add_documents
493
559
  #
@@ -497,6 +563,12 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
497
563
  metadata["updated"] = result
498
564
  metadata["state"] = state
499
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
500
572
  #
501
573
  history_raw = metadata.pop("history", "[]")
502
574
  try:
@@ -545,11 +617,12 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
545
617
  event_data = {
546
618
  "id": index_meta.get("id"),
547
619
  "index_name": index_name,
548
- "state": metadata.get("state"),
620
+ "state": "failed" if error is not None else metadata.get("state"),
549
621
  "error": error,
550
622
  "reindex": is_reindex,
551
623
  "indexed": metadata.get("indexed", 0),
552
624
  "updated": metadata.get("updated", 0),
625
+ "toolkit_id": metadata.get("toolkit_id"),
553
626
  }
554
627
 
555
628
  # Emit the event
@@ -562,57 +635,109 @@ class BaseIndexerToolkit(VectorStoreWrapperBase):
562
635
  except Exception as e:
563
636
  logger.warning(f"Failed to emit index_data_status event: {e}")
564
637
 
638
+ def _emit_index_data_removed_event(self, index_name: str):
639
+ """
640
+ Emit custom event for index data removing.
641
+
642
+ Args:
643
+ index_name: The name of the index
644
+ toolkit_id: The toolkit identifier
645
+ """
646
+ # Build event message
647
+ event_data = {
648
+ "index_name": index_name,
649
+ "toolkit_id": self.toolkit_id,
650
+ "project_id": self.alita.project_id,
651
+ }
652
+ # Emit the event
653
+ try:
654
+ dispatch_custom_event("index_data_removed", event_data)
655
+ logger.debug(
656
+ f"Emitted index_data_removed event for index "
657
+ f"'{index_name}': {event_data}"
658
+ )
659
+ except Exception as e:
660
+ logger.warning(f"Failed to emit index_data_removed event: {e}")
661
+
565
662
  def get_available_tools(self):
566
663
  """
567
664
  Returns the standardized vector search tools (search operations only).
568
665
  Index operations are toolkit-specific and should be added manually to each toolkit.
569
-
666
+
667
+ This method constructs the argument schemas for each tool, merging base parameters with any extra parameters
668
+ defined in the subclass. It also handles the special case for chunking tools and their configuration.
669
+
570
670
  Returns:
571
- List of tool dictionaries with name, ref, description, and args_schema
671
+ list: List of tool dictionaries with name, ref, description, and args_schema.
572
672
  """
673
+ index_params = {
674
+ "index_name": (
675
+ str,
676
+ Field(description="Index name (max 7 characters)", min_length=1, max_length=7)
677
+ ),
678
+ "clean_index": (
679
+ Optional[bool],
680
+ Field(default=False, description="Optional flag to enforce clean existing index before indexing new data")
681
+ ),
682
+ "progress_step": (
683
+ Optional[int],
684
+ Field(default=10, ge=0, le=100, description="Optional step size for progress reporting during indexing")
685
+ ),
686
+ }
687
+ chunking_config = (
688
+ Optional[dict],
689
+ Field(description="Chunking tool configuration", default=loaders_allowed_to_override)
690
+ )
691
+
692
+ index_extra_params = self._index_tool_params() or {}
693
+ chunking_tool = index_extra_params.pop("chunking_tool", None)
694
+ if chunking_tool:
695
+ index_params = {
696
+ **index_params,
697
+ "chunking_tool": chunking_tool,
698
+ }
699
+ index_params["chunking_config"] = chunking_config
700
+ index_args_schema = create_model("IndexData", **index_params, **index_extra_params)
701
+
573
702
  return [
574
703
  {
575
- "name": "index_data",
576
- "mode": "index_data",
704
+ "name": IndexTools.INDEX_DATA.value,
705
+ "mode": IndexTools.INDEX_DATA.value,
577
706
  "ref": self.index_data,
578
707
  "description": "Loads data to index.",
579
- "args_schema": create_model(
580
- "IndexData",
581
- __base__=BaseIndexDataParams,
582
- **self._index_tool_params() if self._index_tool_params() else {}
583
- )
708
+ "args_schema": index_args_schema,
584
709
  },
585
710
  {
586
- "name": "search_index",
587
- "mode": "search_index",
711
+ "name": IndexTools.SEARCH_INDEX.value,
712
+ "mode": IndexTools.SEARCH_INDEX.value,
588
713
  "ref": self.search_index,
589
714
  "description": self.search_index.__doc__,
590
715
  "args_schema": BaseSearchParams
591
716
  },
592
717
  {
593
- "name": "stepback_search_index",
594
- "mode": "stepback_search_index",
718
+ "name": IndexTools.STEPBACK_SEARCH_INDEX.value,
719
+ "mode": IndexTools.STEPBACK_SEARCH_INDEX.value,
595
720
  "ref": self.stepback_search_index,
596
721
  "description": self.stepback_search_index.__doc__,
597
722
  "args_schema": BaseStepbackSearchParams
598
723
  },
599
724
  {
600
- "name": "stepback_summary_index",
601
- "mode": "stepback_summary_index",
725
+ "name": IndexTools.STEPBACK_SUMMARY_INDEX.value,
726
+ "mode": IndexTools.STEPBACK_SUMMARY_INDEX.value,
602
727
  "ref": self.stepback_summary_index,
603
728
  "description": self.stepback_summary_index.__doc__,
604
729
  "args_schema": BaseStepbackSearchParams
605
730
  },
606
731
  {
607
- "name": "remove_index",
608
- "mode": "remove_index",
732
+ "name": IndexTools.REMOVE_INDEX.value,
733
+ "mode": IndexTools.REMOVE_INDEX.value,
609
734
  "ref": self.remove_index,
610
735
  "description": self.remove_index.__doc__,
611
736
  "args_schema": RemoveIndexParams
612
737
  },
613
738
  {
614
- "name": "list_collections",
615
- "mode": "list_collections",
739
+ "name": IndexTools.LIST_COLLECTIONS.value,
740
+ "mode": IndexTools.LIST_COLLECTIONS.value,
616
741
  "ref": self.list_collections,
617
742
  "description": self.list_collections.__doc__,
618
743
  # No parameters
@@ -9,16 +9,17 @@ 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
16
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
16
17
 
17
18
 
18
19
  name = "bitbucket"
19
20
 
20
21
 
21
- def get_tools(tool):
22
+ def get_toolkit(tool):
22
23
  return AlitaBitbucketToolkit.get_toolkit(
23
24
  selected_tools=tool['settings'].get('selected_tools', []),
24
25
  project=tool['settings']['project'],
@@ -33,22 +34,23 @@ def get_tools(tool):
33
34
  doctype='code',
34
35
  embedding_model=tool['settings'].get('embedding_model'),
35
36
  toolkit_name=tool.get('toolkit_name')
36
- ).get_tools()
37
+ )
38
+
39
+ def get_tools(tool):
40
+ return get_toolkit(tool).get_tools()
37
41
 
38
42
 
39
43
  class AlitaBitbucketToolkit(BaseToolkit):
40
44
  tools: List[BaseTool] = []
41
- toolkit_max_length: int = 0
42
45
 
43
46
  @staticmethod
44
47
  def toolkit_config_schema() -> BaseModel:
45
48
  selected_tools = {x['name']: x['args_schema'].schema() for x in
46
49
  BitbucketAPIWrapper.model_construct().get_available_tools()}
47
- AlitaBitbucketToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
48
50
  m = create_model(
49
51
  name,
50
- project=(str, Field(description="Project/Workspace", json_schema_extra={'configuration': True})),
51
- repository=(str, Field(description="Repository", json_schema_extra={'max_toolkit_length': AlitaBitbucketToolkit.toolkit_max_length, 'configuration': True})),
52
+ project=(str, Field(description="Project/Workspace")),
53
+ repository=(str, Field(description="Repository")),
52
54
  branch=(str, Field(description="Main branch", default="main")),
53
55
  cloud=(Optional[bool], Field(description="Hosting Option", default=None)),
54
56
  bitbucket_configuration=(BitbucketConfiguration, Field(description="Bitbucket Configuration", json_schema_extra={'configuration_types': ['bitbucket']})),
@@ -61,7 +63,6 @@ class AlitaBitbucketToolkit(BaseToolkit):
61
63
  'metadata':
62
64
  {
63
65
  "label": "Bitbucket", "icon_url": "bitbucket-icon.svg",
64
- "max_length": AlitaBitbucketToolkit.toolkit_max_length,
65
66
  "categories": ["code repositories"],
66
67
  "extra_categories": ["bitbucket", "git", "repository", "code", "version control"],
67
68
  }
@@ -100,17 +101,21 @@ class AlitaBitbucketToolkit(BaseToolkit):
100
101
  }
101
102
  bitbucket_api_wrapper = BitbucketAPIWrapper(**wrapper_payload)
102
103
  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
104
  tools = []
105
105
  for tool in available_tools:
106
106
  if selected_tools:
107
107
  if tool['name'] not in selected_tools:
108
108
  continue
109
+ description = tool["description"] + f"\nrepo: {bitbucket_api_wrapper.repository}"
110
+ if toolkit_name:
111
+ description = f"{description}\nToolkit: {toolkit_name}"
112
+ description = description[:1000]
109
113
  tools.append(BaseAction(
110
114
  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"]
115
+ name=tool["name"],
116
+ description=description,
117
+ args_schema=tool["args_schema"],
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"]}
114
119
  ))
115
120
  return cls(tools=tools)
116
121