alita-sdk 0.3.257__py3-none-any.whl → 0.3.584__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 (281) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3794 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1073 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/toolkit.py +327 -0
  23. alita_sdk/cli/toolkit_loader.py +85 -0
  24. alita_sdk/cli/tools/__init__.py +43 -0
  25. alita_sdk/cli/tools/approval.py +224 -0
  26. alita_sdk/cli/tools/filesystem.py +1751 -0
  27. alita_sdk/cli/tools/planning.py +389 -0
  28. alita_sdk/cli/tools/terminal.py +414 -0
  29. alita_sdk/community/__init__.py +72 -12
  30. alita_sdk/community/inventory/__init__.py +236 -0
  31. alita_sdk/community/inventory/config.py +257 -0
  32. alita_sdk/community/inventory/enrichment.py +2137 -0
  33. alita_sdk/community/inventory/extractors.py +1469 -0
  34. alita_sdk/community/inventory/ingestion.py +3172 -0
  35. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  36. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  37. alita_sdk/community/inventory/parsers/base.py +295 -0
  38. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  39. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  40. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  41. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  42. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  43. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  44. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  45. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  46. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  47. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  48. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  49. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  50. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  51. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  52. alita_sdk/community/inventory/patterns/loader.py +348 -0
  53. alita_sdk/community/inventory/patterns/registry.py +198 -0
  54. alita_sdk/community/inventory/presets.py +535 -0
  55. alita_sdk/community/inventory/retrieval.py +1403 -0
  56. alita_sdk/community/inventory/toolkit.py +173 -0
  57. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  58. alita_sdk/community/inventory/visualize.py +1370 -0
  59. alita_sdk/configurations/__init__.py +11 -0
  60. alita_sdk/configurations/ado.py +148 -2
  61. alita_sdk/configurations/azure_search.py +1 -1
  62. alita_sdk/configurations/bigquery.py +1 -1
  63. alita_sdk/configurations/bitbucket.py +94 -2
  64. alita_sdk/configurations/browser.py +18 -0
  65. alita_sdk/configurations/carrier.py +19 -0
  66. alita_sdk/configurations/confluence.py +130 -1
  67. alita_sdk/configurations/delta_lake.py +1 -1
  68. alita_sdk/configurations/figma.py +76 -5
  69. alita_sdk/configurations/github.py +65 -1
  70. alita_sdk/configurations/gitlab.py +81 -0
  71. alita_sdk/configurations/google_places.py +17 -0
  72. alita_sdk/configurations/jira.py +103 -0
  73. alita_sdk/configurations/openapi.py +323 -0
  74. alita_sdk/configurations/postman.py +1 -1
  75. alita_sdk/configurations/qtest.py +72 -3
  76. alita_sdk/configurations/report_portal.py +115 -0
  77. alita_sdk/configurations/salesforce.py +19 -0
  78. alita_sdk/configurations/service_now.py +1 -12
  79. alita_sdk/configurations/sharepoint.py +167 -0
  80. alita_sdk/configurations/sonar.py +18 -0
  81. alita_sdk/configurations/sql.py +20 -0
  82. alita_sdk/configurations/testio.py +101 -0
  83. alita_sdk/configurations/testrail.py +88 -0
  84. alita_sdk/configurations/xray.py +94 -1
  85. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  86. alita_sdk/configurations/zephyr_essential.py +95 -0
  87. alita_sdk/runtime/clients/artifact.py +21 -4
  88. alita_sdk/runtime/clients/client.py +458 -67
  89. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  90. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  91. alita_sdk/runtime/clients/sandbox_client.py +352 -0
  92. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  93. alita_sdk/runtime/langchain/assistant.py +183 -43
  94. alita_sdk/runtime/langchain/constants.py +647 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  96. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  97. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
  100. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  101. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  102. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  103. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  104. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  105. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  106. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  107. alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
  108. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  109. alita_sdk/runtime/langchain/langraph_agent.py +493 -105
  110. alita_sdk/runtime/langchain/utils.py +118 -8
  111. alita_sdk/runtime/llms/preloaded.py +2 -6
  112. alita_sdk/runtime/models/mcp_models.py +61 -0
  113. alita_sdk/runtime/skills/__init__.py +91 -0
  114. alita_sdk/runtime/skills/callbacks.py +498 -0
  115. alita_sdk/runtime/skills/discovery.py +540 -0
  116. alita_sdk/runtime/skills/executor.py +610 -0
  117. alita_sdk/runtime/skills/input_builder.py +371 -0
  118. alita_sdk/runtime/skills/models.py +330 -0
  119. alita_sdk/runtime/skills/registry.py +355 -0
  120. alita_sdk/runtime/skills/skill_runner.py +330 -0
  121. alita_sdk/runtime/toolkits/__init__.py +28 -0
  122. alita_sdk/runtime/toolkits/application.py +14 -4
  123. alita_sdk/runtime/toolkits/artifact.py +25 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +782 -0
  126. alita_sdk/runtime/toolkits/planning.py +178 -0
  127. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  128. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  129. alita_sdk/runtime/toolkits/tools.py +314 -70
  130. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  131. alita_sdk/runtime/tools/__init__.py +24 -0
  132. alita_sdk/runtime/tools/application.py +16 -4
  133. alita_sdk/runtime/tools/artifact.py +367 -33
  134. alita_sdk/runtime/tools/data_analysis.py +183 -0
  135. alita_sdk/runtime/tools/function.py +100 -4
  136. alita_sdk/runtime/tools/graph.py +81 -0
  137. alita_sdk/runtime/tools/image_generation.py +218 -0
  138. alita_sdk/runtime/tools/llm.py +1032 -177
  139. alita_sdk/runtime/tools/loop.py +3 -1
  140. alita_sdk/runtime/tools/loop_output.py +3 -1
  141. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  142. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  143. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  144. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  145. alita_sdk/runtime/tools/planning/models.py +246 -0
  146. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  147. alita_sdk/runtime/tools/router.py +2 -1
  148. alita_sdk/runtime/tools/sandbox.py +375 -0
  149. alita_sdk/runtime/tools/skill_router.py +776 -0
  150. alita_sdk/runtime/tools/tool.py +3 -1
  151. alita_sdk/runtime/tools/vectorstore.py +69 -65
  152. alita_sdk/runtime/tools/vectorstore_base.py +163 -90
  153. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  154. alita_sdk/runtime/utils/constants.py +5 -1
  155. alita_sdk/runtime/utils/mcp_client.py +492 -0
  156. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  157. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  158. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  159. alita_sdk/runtime/utils/streamlit.py +41 -14
  160. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  161. alita_sdk/runtime/utils/utils.py +48 -0
  162. alita_sdk/tools/__init__.py +135 -37
  163. alita_sdk/tools/ado/__init__.py +2 -2
  164. alita_sdk/tools/ado/repos/__init__.py +16 -19
  165. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  166. alita_sdk/tools/ado/test_plan/__init__.py +27 -8
  167. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  168. alita_sdk/tools/ado/wiki/__init__.py +28 -12
  169. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  170. alita_sdk/tools/ado/work_item/__init__.py +28 -12
  171. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  172. alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
  173. alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
  174. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  175. alita_sdk/tools/azure_ai/search/__init__.py +14 -8
  176. alita_sdk/tools/base/tool.py +5 -1
  177. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  178. alita_sdk/tools/bitbucket/__init__.py +28 -19
  179. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  180. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  181. alita_sdk/tools/browser/__init__.py +41 -16
  182. alita_sdk/tools/browser/crawler.py +3 -1
  183. alita_sdk/tools/browser/utils.py +15 -6
  184. alita_sdk/tools/carrier/__init__.py +18 -17
  185. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  186. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  187. alita_sdk/tools/chunkers/__init__.py +3 -1
  188. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  189. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  190. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  191. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  192. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  193. alita_sdk/tools/cloud/aws/__init__.py +12 -7
  194. alita_sdk/tools/cloud/azure/__init__.py +12 -7
  195. alita_sdk/tools/cloud/gcp/__init__.py +12 -7
  196. alita_sdk/tools/cloud/k8s/__init__.py +12 -7
  197. alita_sdk/tools/code/linter/__init__.py +10 -8
  198. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  199. alita_sdk/tools/code/sonar/__init__.py +21 -13
  200. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  201. alita_sdk/tools/confluence/__init__.py +22 -14
  202. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  203. alita_sdk/tools/confluence/loader.py +14 -2
  204. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  205. alita_sdk/tools/elastic/__init__.py +11 -8
  206. alita_sdk/tools/elitea_base.py +546 -64
  207. alita_sdk/tools/figma/__init__.py +60 -11
  208. alita_sdk/tools/figma/api_wrapper.py +1400 -167
  209. alita_sdk/tools/figma/figma_client.py +73 -0
  210. alita_sdk/tools/figma/toon_tools.py +2748 -0
  211. alita_sdk/tools/github/__init__.py +18 -17
  212. alita_sdk/tools/github/api_wrapper.py +9 -26
  213. alita_sdk/tools/github/github_client.py +81 -12
  214. alita_sdk/tools/github/schemas.py +2 -1
  215. alita_sdk/tools/github/tool.py +5 -1
  216. alita_sdk/tools/gitlab/__init__.py +19 -13
  217. alita_sdk/tools/gitlab/api_wrapper.py +256 -80
  218. alita_sdk/tools/gitlab_org/__init__.py +14 -10
  219. alita_sdk/tools/google/bigquery/__init__.py +14 -13
  220. alita_sdk/tools/google/bigquery/tool.py +5 -1
  221. alita_sdk/tools/google_places/__init__.py +21 -11
  222. alita_sdk/tools/jira/__init__.py +22 -11
  223. alita_sdk/tools/jira/api_wrapper.py +315 -168
  224. alita_sdk/tools/keycloak/__init__.py +11 -8
  225. alita_sdk/tools/localgit/__init__.py +9 -3
  226. alita_sdk/tools/localgit/local_git.py +62 -54
  227. alita_sdk/tools/localgit/tool.py +5 -1
  228. alita_sdk/tools/memory/__init__.py +38 -14
  229. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  230. alita_sdk/tools/ocr/__init__.py +11 -8
  231. alita_sdk/tools/openapi/__init__.py +491 -106
  232. alita_sdk/tools/openapi/api_wrapper.py +1357 -0
  233. alita_sdk/tools/openapi/tool.py +20 -0
  234. alita_sdk/tools/pandas/__init__.py +20 -12
  235. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  236. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  237. alita_sdk/tools/postman/__init__.py +11 -11
  238. alita_sdk/tools/postman/api_wrapper.py +19 -8
  239. alita_sdk/tools/postman/postman_analysis.py +8 -1
  240. alita_sdk/tools/pptx/__init__.py +11 -10
  241. alita_sdk/tools/qtest/__init__.py +22 -14
  242. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  243. alita_sdk/tools/rally/__init__.py +13 -10
  244. alita_sdk/tools/report_portal/__init__.py +23 -16
  245. alita_sdk/tools/salesforce/__init__.py +22 -16
  246. alita_sdk/tools/servicenow/__init__.py +21 -16
  247. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  248. alita_sdk/tools/sharepoint/__init__.py +17 -14
  249. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  250. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  251. alita_sdk/tools/sharepoint/utils.py +8 -2
  252. alita_sdk/tools/slack/__init__.py +13 -8
  253. alita_sdk/tools/sql/__init__.py +22 -19
  254. alita_sdk/tools/sql/api_wrapper.py +71 -23
  255. alita_sdk/tools/testio/__init__.py +21 -13
  256. alita_sdk/tools/testrail/__init__.py +13 -11
  257. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  258. alita_sdk/tools/utils/__init__.py +28 -4
  259. alita_sdk/tools/utils/content_parser.py +241 -55
  260. alita_sdk/tools/utils/text_operations.py +254 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  262. alita_sdk/tools/xray/__init__.py +18 -14
  263. alita_sdk/tools/xray/api_wrapper.py +58 -113
  264. alita_sdk/tools/yagmail/__init__.py +9 -3
  265. alita_sdk/tools/zephyr/__init__.py +12 -7
  266. alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
  267. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  268. alita_sdk/tools/zephyr_essential/__init__.py +16 -10
  269. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  270. alita_sdk/tools/zephyr_essential/client.py +6 -4
  271. alita_sdk/tools/zephyr_scale/__init__.py +13 -8
  272. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  273. alita_sdk/tools/zephyr_squad/__init__.py +12 -7
  274. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
  275. alita_sdk-0.3.584.dist-info/RECORD +452 -0
  276. alita_sdk-0.3.584.dist-info/entry_points.txt +2 -0
  277. alita_sdk/tools/bitbucket/tools.py +0 -304
  278. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  279. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/WHEEL +0 -0
  280. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
  281. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/top_level.txt +0 -0
@@ -4,28 +4,39 @@ from langchain_core.tools import BaseTool, BaseToolkit
4
4
  from pydantic import create_model, BaseModel, Field
5
5
 
6
6
  import requests
7
+
8
+ from ...elitea_base import filter_missconfigured_index_tools
7
9
  from ....configurations.ado import AdoConfiguration
8
10
  from ....configurations.pgvector import PgVectorConfiguration
9
11
  from ...base.tool import BaseAction
10
- 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
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
11
14
 
12
15
  name = "ado_boards"
13
16
 
17
+ def get_toolkit(tool):
18
+ return AzureDevOpsWorkItemsToolkit().get_toolkit(
19
+ selected_tools=tool['settings'].get('selected_tools', []),
20
+ ado_configuration=tool['settings']['ado_configuration'],
21
+ limit=tool['settings'].get('limit', 5),
22
+ toolkit_name=tool.get('toolkit_name', ''),
23
+ alita=tool['settings'].get('alita', None),
24
+ llm=tool['settings'].get('llm', None),
25
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
26
+ collection_name=tool['toolkit_name'],
27
+ doctype='doc',
28
+ embedding_model=tool['settings'].get('embedding_model'),
29
+ vectorstore_type="PGVector"
30
+ )
31
+
14
32
  class AzureDevOpsWorkItemsToolkit(BaseToolkit):
15
33
  tools: List[BaseTool] = []
16
- toolkit_max_length: int = 0
17
34
 
18
35
  @staticmethod
19
36
  def toolkit_config_schema() -> BaseModel:
20
37
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
21
- AzureDevOpsWorkItemsToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
22
38
  m = create_model(
23
39
  name,
24
- name=(str, Field(description="Toolkit name",
25
- json_schema_extra={
26
- 'toolkit_name': True,
27
- 'max_toolkit_length': AzureDevOpsWorkItemsToolkit.toolkit_max_length})
28
- ),
29
40
  ado_configuration=(AdoConfiguration, Field(description="Ado Work Item configuration", json_schema_extra={'configuration_types': ['ado']})),
30
41
  limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
31
42
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
@@ -77,6 +88,7 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
77
88
  return m
78
89
 
79
90
  @classmethod
91
+ @filter_missconfigured_index_tools
80
92
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
81
93
  from os import environ
82
94
  if not environ.get('AZURE_DEVOPS_CACHE_DIR', None):
@@ -93,16 +105,20 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
93
105
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
94
106
  available_tools = azure_devops_api_wrapper.get_available_tools()
95
107
  tools = []
96
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
97
108
  for tool in available_tools:
98
109
  if selected_tools:
99
110
  if tool["name"] not in selected_tools:
100
111
  continue
112
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
113
+ if toolkit_name:
114
+ description = f"{description}\nToolkit: {toolkit_name}"
115
+ description = description[:1000]
101
116
  tools.append(BaseAction(
102
117
  api_wrapper=azure_devops_api_wrapper,
103
- name=prefix + tool["name"],
104
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
105
- args_schema=tool["args_schema"]
118
+ name=tool["name"],
119
+ description=description,
120
+ args_schema=tool["args_schema"],
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"]}
106
122
  ))
107
123
  return cls(tools=tools)
108
124
 
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import logging
3
+ import re
3
4
  import urllib.parse
4
5
  from typing import Dict, List, Generator, Optional
5
6
 
@@ -7,6 +8,7 @@ from azure.devops.connection import Connection
7
8
  from azure.devops.v7_1.core import CoreClient
8
9
  from azure.devops.v7_1.wiki import WikiClient
9
10
  from azure.devops.v7_1.work_item_tracking import TeamContext, Wiql, WorkItemTrackingClient
11
+ from bs4 import BeautifulSoup
10
12
  from langchain_core.documents import Document
11
13
  from langchain_core.tools import ToolException
12
14
  from msrest.authentication import BasicAuthentication
@@ -15,6 +17,8 @@ from pydantic import model_validator
15
17
  from pydantic.fields import Field
16
18
 
17
19
  from alita_sdk.tools.non_code_indexer_toolkit import NonCodeIndexerToolkit
20
+ from ...utils.content_parser import parse_file_content
21
+ from ....runtime.utils.utils import IndexerKeywords
18
22
 
19
23
  logger = logging.getLogger(__name__)
20
24
 
@@ -52,7 +56,11 @@ ADOGetWorkItem = create_model(
52
56
  id=(int, Field(description="The work item id")),
53
57
  fields=(Optional[list[str]], Field(description="Comma-separated list of requested fields", default=None)),
54
58
  as_of=(Optional[str], Field(description="AsOf UTC date time string", default=None)),
55
- expand=(Optional[str], Field(description="The expand parameters for work item attributes. Possible options are { None, Relations, Fields, Links, All }.", default=None))
59
+ expand=(Optional[str], Field(description="The expand parameters for work item attributes. Possible options are { None, Relations, Fields, Links, All }.", default=None)),
60
+ parse_attachments=(Optional[bool], Field(description="Value that defines is attachment should be parsed.", default=False)),
61
+ image_description_prompt=(Optional[str],
62
+ Field(description="Prompt which is used for image description", default=None)),
63
+
56
64
  )
57
65
 
58
66
  ADOLinkWorkItem = create_model(
@@ -283,8 +291,23 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
283
291
  logger.error(f"Error searching work items: {e}")
284
292
  return ToolException(f"Error searching work items: {e}")
285
293
 
286
-
287
- def get_work_item(self, id: int, fields: Optional[list[str]] = None, as_of: Optional[str] = None, expand: Optional[str] = None):
294
+ def parse_attachment_by_url(self, attachment_url, file_name=None, image_description_prompt=None):
295
+ match = re.search(r'attachments/([\w-]+)(?:\?fileName=([^&]+))?', attachment_url)
296
+ if match:
297
+ attachment_id = match.group(1)
298
+ if not file_name:
299
+ file_name = match.group(2)
300
+ if not file_name:
301
+ raise ToolException("File name must be provided either in the URL or as a parameter.")
302
+ return self.parse_attachment_by_id(attachment_id, file_name, image_description_prompt)
303
+ raise ToolException(f"Attachment '{attachment_url}' was not found.")
304
+
305
+ def parse_attachment_by_id(self, attachment_id, file_name, image_description_prompt):
306
+ file_content = self.get_attachment_content(attachment_id)
307
+ return parse_file_content(file_content=file_content, file_name=file_name,
308
+ llm=self.llm, prompt=image_description_prompt)
309
+
310
+ def get_work_item(self, id: int, fields: Optional[list[str]] = None, as_of: Optional[str] = None, expand: Optional[str] = None, parse_attachments=False, image_description_prompt=None):
288
311
  """Get a single work item by ID."""
289
312
  try:
290
313
  # Validate that the Azure DevOps client is initialized
@@ -306,11 +329,38 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
306
329
  parsed_item.update(fields_data)
307
330
 
308
331
  # extract relations if any
309
- relations_data = work_item.relations
332
+ relations_data = None
333
+ if expand and str(expand).lower() in ("relations", "all"):
334
+ try:
335
+ relations_data = getattr(work_item, 'relations', None)
336
+ except KeyError:
337
+ relations_data = None
310
338
  if relations_data:
311
- parsed_item['relations'] = []
312
- for relation in relations_data:
313
- parsed_item['relations'].append(relation.as_dict())
339
+ parsed_item['relations'] = [relation.as_dict() for relation in relations_data]
340
+
341
+ if parse_attachments:
342
+ # describe images in work item fields if present
343
+ for field_name, field_value in fields_data.items():
344
+ if isinstance(field_value, str):
345
+ soup = BeautifulSoup(field_value, 'html.parser')
346
+ images = soup.find_all('img')
347
+ for img in images:
348
+ src = img.get('src')
349
+ if src:
350
+ description = self.parse_attachment_by_url(src, image_description_prompt=image_description_prompt)
351
+ img['image-description'] = description
352
+ parsed_item[field_name] = str(soup)
353
+ # parse attached documents if present
354
+ for relation in parsed_item.get('relations', []):
355
+ # Only process actual file attachments
356
+ if relation.get('rel') == 'AttachedFile':
357
+ file_name = relation.get('attributes', {}).get('name')
358
+ if file_name:
359
+ try:
360
+ relation['content'] = self.parse_attachment_by_url(relation['url'], file_name, image_description_prompt=image_description_prompt)
361
+ except Exception as att_e:
362
+ logger.warning(f"Failed to parse attachment {file_name}: {att_e}")
363
+
314
364
 
315
365
  return parsed_item
316
366
  except Exception as e:
@@ -521,11 +571,45 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
521
571
  'attachment_ids': {rel.url.split('/')[-1]:rel.attributes.get('name', '') for rel in wi.relations or [] if rel.rel == 'AttachedFile'}
522
572
  })
523
573
 
574
+ def get_attachment_content(self, attachment_id):
575
+ content_generator = self._client.get_attachment_content(id=attachment_id, download=True)
576
+ return b"".join(content_generator)
577
+
524
578
  def _process_document(self, document: Document) -> Generator[Document, None, None]:
525
- for attachment_id, file_name in document.metadata.get('attachment_ids', {}).items():
526
- content_generator = self._client.get_attachment_content(id=attachment_id, download=True)
527
- content = b"".join(x for x in content_generator)
528
- yield Document(page_content="", metadata={'id': attachment_id, 'loader_content_type': file_name, 'loader_content': content})
579
+ raw_attachment_ids = document.metadata.get('attachment_ids', {})
580
+
581
+ # Normalize attachment_ids: accept dict or JSON string, raise otherwise
582
+ if isinstance(raw_attachment_ids, str):
583
+ try:
584
+ loaded = json.loads(raw_attachment_ids)
585
+ except json.JSONDecodeError:
586
+ raise TypeError(
587
+ f"Expected dict or JSON string for 'attachment_ids', got non-JSON string for id="
588
+ f"{document.metadata.get('id')}: {raw_attachment_ids!r}"
589
+ )
590
+ if not isinstance(loaded, dict):
591
+ raise TypeError(
592
+ f"'attachment_ids' JSON did not decode to dict for id={document.metadata.get('id')}: {loaded!r}"
593
+ )
594
+ attachment_ids = loaded
595
+ elif isinstance(raw_attachment_ids, dict):
596
+ attachment_ids = raw_attachment_ids
597
+ else:
598
+ raise TypeError(
599
+ f"Expected 'attachment_ids' to be dict or JSON string, got {type(raw_attachment_ids)} "
600
+ f"for id={document.metadata.get('id')}: {raw_attachment_ids!r}"
601
+ )
602
+
603
+ for attachment_id, file_name in attachment_ids.items():
604
+ content = self.get_attachment_content(attachment_id=attachment_id)
605
+ yield Document(
606
+ page_content="",
607
+ metadata={
608
+ 'id': attachment_id,
609
+ IndexerKeywords.CONTENT_FILE_NAME.value: file_name,
610
+ IndexerKeywords.CONTENT_IN_BYTES.value: content,
611
+ },
612
+ )
529
613
 
530
614
  def _index_tool_params(self):
531
615
  """Return the parameters for indexing data."""
@@ -5,7 +5,9 @@ from pydantic import create_model, BaseModel, Field, SecretStr
5
5
 
6
6
  from .data_mining_wrapper import AdvancedJiraMiningWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..elitea_base import filter_missconfigured_index_tools
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
9
11
 
10
12
  name = "advanced_jira_mining"
11
13
 
@@ -27,15 +29,13 @@ def get_tools(tool):
27
29
 
28
30
  class AdvancedJiraMiningToolkit(BaseToolkit):
29
31
  tools: List[BaseTool] = []
30
- toolkit_max_length: int = 0
31
32
 
32
33
  @staticmethod
33
34
  def toolkit_config_schema() -> BaseModel:
34
35
  selected_tools = {x['name']: x['args_schema'].schema() for x in AdvancedJiraMiningWrapper.model_construct().get_available_tools()}
35
- AdvancedJiraMiningToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
36
36
  return create_model(
37
37
  name,
38
- 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})),
38
+ jira_base_url=(str, Field(default="", title="Jira URL", description="Jira URL", json_schema_extra={'toolkit_name': True})),
39
39
  confluence_base_url=(str, Field(default="", title="Confluence URL", description="Confluence URL")),
40
40
  model_type=(str, Field(default="", title="Model type", description="Model type")),
41
41
  summarization_prompt=(Optional[str], Field(default=None, title="Summarization prompt", description="Summarization prompt")),
@@ -59,22 +59,27 @@ class AdvancedJiraMiningToolkit(BaseToolkit):
59
59
  )
60
60
 
61
61
  @classmethod
62
+ @filter_missconfigured_index_tools
62
63
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
63
64
  if selected_tools is None:
64
65
  selected_tools = []
65
66
  jira_mining_wrapper = AdvancedJiraMiningWrapper(**kwargs)
66
67
  available_tools = jira_mining_wrapper.get_available_tools()
67
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
68
68
  tools = []
69
69
  for tool in available_tools:
70
70
  if selected_tools:
71
71
  if tool["name"] not in selected_tools:
72
72
  continue
73
+ description = tool["description"]
74
+ if toolkit_name:
75
+ description = f"Toolkit: {toolkit_name}\n{description}"
76
+ description = description[:1000]
73
77
  tools.append(BaseAction(
74
78
  api_wrapper=jira_mining_wrapper,
75
- name=prefix + tool["name"],
76
- description=tool["description"],
77
- args_schema=tool["args_schema"]
79
+ name=tool["name"],
80
+ description=description,
81
+ args_schema=tool["args_schema"],
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"]}
78
83
  ))
79
84
  return cls(tools=tools)
80
85
 
@@ -3,12 +3,13 @@ from functools import lru_cache
3
3
  from typing import List, Optional, Type
4
4
 
5
5
  from langchain_core.tools import BaseTool, BaseToolkit
6
- from pydantic import BaseModel, Field, SecretStr, computed_field, field_validator
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
@@ -54,7 +51,7 @@ class DeltaLakeToolkitConfig(BaseModel):
54
51
  }
55
52
  }
56
53
 
57
- delta_lake_configuration: Optional[DeltaLakeConfiguration] = Field(description="Delta Lake Configuration", json_schema_extra={"configuration_types": ["delta_lake"]})
54
+ delta_lake_configuration: DeltaLakeConfiguration = Field(description="Delta Lake Configuration", json_schema_extra={"configuration_types": ["delta_lake"]})
58
55
  selected_tools: List[str] = Field(default=[], description="Selected tools", json_schema_extra={"args_schemas": get_available_tools()})
59
56
 
60
57
  @field_validator("selected_tools", mode="before", check_fields=False)
@@ -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()}"
@@ -4,9 +4,12 @@ from .api_wrapper import AzureSearchApiWrapper
4
4
  from ...base.tool import BaseAction
5
5
  from langchain_core.tools import BaseToolkit, BaseTool
6
6
  from pydantic import create_model, BaseModel, ConfigDict, Field
7
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
7
+
8
+ from ...elitea_base import filter_missconfigured_index_tools
9
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
8
10
  from ....configurations.azure_search import AzureSearchConfiguration
9
11
  import requests
12
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
10
13
 
11
14
  logger = getLogger(__name__)
12
15
 
@@ -29,17 +32,15 @@ def get_toolkit():
29
32
 
30
33
  class AzureSearchToolkit(BaseToolkit):
31
34
  tools: List[BaseTool] = []
32
- toolkit_max_length: int = 0
33
35
 
34
36
  @staticmethod
35
37
  def toolkit_config_schema() -> BaseModel:
36
38
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureSearchApiWrapper.model_construct().get_available_tools()}
37
- AzureSearchToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
38
39
  m = create_model(
39
40
  name,
40
41
  index_name=(str, Field(description="Azure Search index name")),
41
42
  azure_search_configuration=(
42
- Optional[AzureSearchConfiguration],
43
+ AzureSearchConfiguration,
43
44
  Field(description="Azure Search Configuration", json_schema_extra={'configuration_types': ['azure_search']})
44
45
  ),
45
46
  api_version=(Optional[str], Field(description="API version", default=None)),
@@ -66,6 +67,7 @@ class AzureSearchToolkit(BaseToolkit):
66
67
  return m
67
68
 
68
69
  @classmethod
70
+ @filter_missconfigured_index_tools
69
71
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
70
72
  if selected_tools is None:
71
73
  selected_tools = []
@@ -76,17 +78,21 @@ class AzureSearchToolkit(BaseToolkit):
76
78
  }
77
79
  azure_search_api_wrapper = AzureSearchApiWrapper(**wrapper_payload)
78
80
  available_tools = azure_search_api_wrapper.get_available_tools()
79
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
80
81
  tools = []
81
82
  for tool in available_tools:
82
83
  if selected_tools:
83
84
  if tool["name"] not in selected_tools:
84
85
  continue
86
+ description = tool["description"]
87
+ if toolkit_name:
88
+ description = f"Toolkit: {toolkit_name}\n{description}"
89
+ description = description[:1000]
85
90
  tools.append(BaseAction(
86
91
  api_wrapper=azure_search_api_wrapper,
87
- name=prefix + tool["name"],
88
- description=tool["description"],
89
- 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"]}
90
96
  ))
91
97
  return cls(tools=tools)
92
98
 
@@ -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}")