alita-sdk 0.3.379__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 (278) 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 +156 -0
  6. alita_sdk/cli/agent_loader.py +245 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3113 -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/testcases/__init__.py +94 -0
  23. alita_sdk/cli/testcases/data_generation.py +119 -0
  24. alita_sdk/cli/testcases/discovery.py +96 -0
  25. alita_sdk/cli/testcases/executor.py +84 -0
  26. alita_sdk/cli/testcases/logger.py +85 -0
  27. alita_sdk/cli/testcases/parser.py +172 -0
  28. alita_sdk/cli/testcases/prompts.py +91 -0
  29. alita_sdk/cli/testcases/reporting.py +125 -0
  30. alita_sdk/cli/testcases/setup.py +108 -0
  31. alita_sdk/cli/testcases/test_runner.py +282 -0
  32. alita_sdk/cli/testcases/utils.py +39 -0
  33. alita_sdk/cli/testcases/validation.py +90 -0
  34. alita_sdk/cli/testcases/workflow.py +196 -0
  35. alita_sdk/cli/toolkit.py +327 -0
  36. alita_sdk/cli/toolkit_loader.py +85 -0
  37. alita_sdk/cli/tools/__init__.py +43 -0
  38. alita_sdk/cli/tools/approval.py +224 -0
  39. alita_sdk/cli/tools/filesystem.py +1751 -0
  40. alita_sdk/cli/tools/planning.py +389 -0
  41. alita_sdk/cli/tools/terminal.py +414 -0
  42. alita_sdk/community/__init__.py +72 -12
  43. alita_sdk/community/inventory/__init__.py +236 -0
  44. alita_sdk/community/inventory/config.py +257 -0
  45. alita_sdk/community/inventory/enrichment.py +2137 -0
  46. alita_sdk/community/inventory/extractors.py +1469 -0
  47. alita_sdk/community/inventory/ingestion.py +3172 -0
  48. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  49. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  50. alita_sdk/community/inventory/parsers/base.py +295 -0
  51. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  52. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  53. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  54. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  55. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  56. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  57. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  58. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  59. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  60. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  61. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  62. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  63. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  64. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  65. alita_sdk/community/inventory/patterns/loader.py +348 -0
  66. alita_sdk/community/inventory/patterns/registry.py +198 -0
  67. alita_sdk/community/inventory/presets.py +535 -0
  68. alita_sdk/community/inventory/retrieval.py +1403 -0
  69. alita_sdk/community/inventory/toolkit.py +173 -0
  70. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  71. alita_sdk/community/inventory/visualize.py +1370 -0
  72. alita_sdk/configurations/__init__.py +1 -1
  73. alita_sdk/configurations/ado.py +141 -20
  74. alita_sdk/configurations/bitbucket.py +94 -2
  75. alita_sdk/configurations/confluence.py +130 -1
  76. alita_sdk/configurations/figma.py +76 -0
  77. alita_sdk/configurations/gitlab.py +91 -0
  78. alita_sdk/configurations/jira.py +103 -0
  79. alita_sdk/configurations/openapi.py +329 -0
  80. alita_sdk/configurations/qtest.py +72 -1
  81. alita_sdk/configurations/report_portal.py +96 -0
  82. alita_sdk/configurations/sharepoint.py +148 -0
  83. alita_sdk/configurations/testio.py +83 -0
  84. alita_sdk/configurations/testrail.py +88 -0
  85. alita_sdk/configurations/xray.py +93 -0
  86. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  87. alita_sdk/configurations/zephyr_essential.py +75 -0
  88. alita_sdk/runtime/clients/artifact.py +3 -3
  89. alita_sdk/runtime/clients/client.py +388 -46
  90. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  91. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  92. alita_sdk/runtime/clients/sandbox_client.py +8 -21
  93. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  94. alita_sdk/runtime/langchain/assistant.py +157 -39
  95. alita_sdk/runtime/langchain/constants.py +647 -1
  96. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  97. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
  100. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  102. alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
  103. alita_sdk/runtime/langchain/langraph_agent.py +405 -84
  104. alita_sdk/runtime/langchain/utils.py +106 -7
  105. alita_sdk/runtime/llms/preloaded.py +2 -6
  106. alita_sdk/runtime/models/mcp_models.py +61 -0
  107. alita_sdk/runtime/skills/__init__.py +91 -0
  108. alita_sdk/runtime/skills/callbacks.py +498 -0
  109. alita_sdk/runtime/skills/discovery.py +540 -0
  110. alita_sdk/runtime/skills/executor.py +610 -0
  111. alita_sdk/runtime/skills/input_builder.py +371 -0
  112. alita_sdk/runtime/skills/models.py +330 -0
  113. alita_sdk/runtime/skills/registry.py +355 -0
  114. alita_sdk/runtime/skills/skill_runner.py +330 -0
  115. alita_sdk/runtime/toolkits/__init__.py +31 -0
  116. alita_sdk/runtime/toolkits/application.py +29 -10
  117. alita_sdk/runtime/toolkits/artifact.py +20 -11
  118. alita_sdk/runtime/toolkits/datasource.py +13 -6
  119. alita_sdk/runtime/toolkits/mcp.py +783 -0
  120. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  121. alita_sdk/runtime/toolkits/planning.py +178 -0
  122. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  123. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  124. alita_sdk/runtime/toolkits/tools.py +356 -69
  125. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  126. alita_sdk/runtime/tools/__init__.py +10 -3
  127. alita_sdk/runtime/tools/application.py +27 -6
  128. alita_sdk/runtime/tools/artifact.py +511 -28
  129. alita_sdk/runtime/tools/data_analysis.py +183 -0
  130. alita_sdk/runtime/tools/function.py +67 -35
  131. alita_sdk/runtime/tools/graph.py +10 -4
  132. alita_sdk/runtime/tools/image_generation.py +148 -46
  133. alita_sdk/runtime/tools/llm.py +1003 -128
  134. alita_sdk/runtime/tools/loop.py +3 -1
  135. alita_sdk/runtime/tools/loop_output.py +3 -1
  136. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  137. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  138. alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
  139. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  140. alita_sdk/runtime/tools/planning/models.py +246 -0
  141. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  142. alita_sdk/runtime/tools/router.py +2 -4
  143. alita_sdk/runtime/tools/sandbox.py +65 -48
  144. alita_sdk/runtime/tools/skill_router.py +776 -0
  145. alita_sdk/runtime/tools/tool.py +3 -1
  146. alita_sdk/runtime/tools/vectorstore.py +9 -3
  147. alita_sdk/runtime/tools/vectorstore_base.py +70 -14
  148. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  149. alita_sdk/runtime/utils/constants.py +5 -1
  150. alita_sdk/runtime/utils/mcp_client.py +492 -0
  151. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  152. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  153. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  154. alita_sdk/runtime/utils/serialization.py +155 -0
  155. alita_sdk/runtime/utils/streamlit.py +40 -13
  156. alita_sdk/runtime/utils/toolkit_utils.py +30 -9
  157. alita_sdk/runtime/utils/utils.py +36 -0
  158. alita_sdk/tools/__init__.py +134 -35
  159. alita_sdk/tools/ado/repos/__init__.py +51 -32
  160. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  161. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  162. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  163. alita_sdk/tools/ado/utils.py +1 -18
  164. alita_sdk/tools/ado/wiki/__init__.py +25 -12
  165. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  166. alita_sdk/tools/ado/work_item/__init__.py +26 -13
  167. alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
  168. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  169. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  170. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  171. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  172. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  173. alita_sdk/tools/base/tool.py +5 -1
  174. alita_sdk/tools/base_indexer_toolkit.py +271 -84
  175. alita_sdk/tools/bitbucket/__init__.py +17 -11
  176. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  177. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  178. alita_sdk/tools/browser/__init__.py +5 -4
  179. alita_sdk/tools/carrier/__init__.py +5 -6
  180. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  181. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  182. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  183. alita_sdk/tools/chunkers/__init__.py +3 -1
  184. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  185. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  186. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  187. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  188. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  189. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  190. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  191. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  192. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  193. alita_sdk/tools/code/linter/__init__.py +10 -8
  194. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  195. alita_sdk/tools/code/sonar/__init__.py +11 -8
  196. alita_sdk/tools/code_indexer_toolkit.py +82 -22
  197. alita_sdk/tools/confluence/__init__.py +22 -16
  198. alita_sdk/tools/confluence/api_wrapper.py +107 -30
  199. alita_sdk/tools/confluence/loader.py +14 -2
  200. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  201. alita_sdk/tools/elastic/__init__.py +11 -8
  202. alita_sdk/tools/elitea_base.py +493 -30
  203. alita_sdk/tools/figma/__init__.py +58 -11
  204. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  205. alita_sdk/tools/figma/figma_client.py +73 -0
  206. alita_sdk/tools/figma/toon_tools.py +2748 -0
  207. alita_sdk/tools/github/__init__.py +14 -15
  208. alita_sdk/tools/github/github_client.py +224 -100
  209. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  210. alita_sdk/tools/github/schemas.py +14 -5
  211. alita_sdk/tools/github/tool.py +5 -1
  212. alita_sdk/tools/github/tool_prompts.py +9 -22
  213. alita_sdk/tools/gitlab/__init__.py +16 -11
  214. alita_sdk/tools/gitlab/api_wrapper.py +218 -48
  215. alita_sdk/tools/gitlab_org/__init__.py +10 -9
  216. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  217. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  218. alita_sdk/tools/google/bigquery/tool.py +5 -1
  219. alita_sdk/tools/google_places/__init__.py +11 -8
  220. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  221. alita_sdk/tools/jira/__init__.py +17 -10
  222. alita_sdk/tools/jira/api_wrapper.py +92 -41
  223. alita_sdk/tools/keycloak/__init__.py +11 -8
  224. alita_sdk/tools/localgit/__init__.py +9 -3
  225. alita_sdk/tools/localgit/local_git.py +62 -54
  226. alita_sdk/tools/localgit/tool.py +5 -1
  227. alita_sdk/tools/memory/__init__.py +12 -4
  228. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  229. alita_sdk/tools/ocr/__init__.py +11 -8
  230. alita_sdk/tools/openapi/__init__.py +491 -106
  231. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  232. alita_sdk/tools/openapi/tool.py +20 -0
  233. alita_sdk/tools/pandas/__init__.py +20 -12
  234. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  235. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  236. alita_sdk/tools/postman/__init__.py +10 -9
  237. alita_sdk/tools/pptx/__init__.py +11 -10
  238. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  239. alita_sdk/tools/qtest/__init__.py +31 -11
  240. alita_sdk/tools/qtest/api_wrapper.py +2135 -86
  241. alita_sdk/tools/rally/__init__.py +10 -9
  242. alita_sdk/tools/rally/api_wrapper.py +1 -1
  243. alita_sdk/tools/report_portal/__init__.py +12 -8
  244. alita_sdk/tools/salesforce/__init__.py +10 -8
  245. alita_sdk/tools/servicenow/__init__.py +17 -15
  246. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  247. alita_sdk/tools/sharepoint/__init__.py +10 -7
  248. alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
  249. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  250. alita_sdk/tools/sharepoint/utils.py +8 -2
  251. alita_sdk/tools/slack/__init__.py +10 -7
  252. alita_sdk/tools/slack/api_wrapper.py +2 -2
  253. alita_sdk/tools/sql/__init__.py +12 -9
  254. alita_sdk/tools/testio/__init__.py +10 -7
  255. alita_sdk/tools/testrail/__init__.py +11 -10
  256. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  257. alita_sdk/tools/utils/__init__.py +9 -4
  258. alita_sdk/tools/utils/content_parser.py +103 -18
  259. alita_sdk/tools/utils/text_operations.py +410 -0
  260. alita_sdk/tools/utils/tool_prompts.py +79 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
  262. alita_sdk/tools/xray/__init__.py +13 -9
  263. alita_sdk/tools/yagmail/__init__.py +9 -3
  264. alita_sdk/tools/zephyr/__init__.py +10 -7
  265. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
  266. alita_sdk/tools/zephyr_essential/__init__.py +10 -7
  267. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  268. alita_sdk/tools/zephyr_essential/client.py +2 -2
  269. alita_sdk/tools/zephyr_scale/__init__.py +11 -8
  270. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  271. alita_sdk/tools/zephyr_squad/__init__.py +10 -7
  272. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
  273. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  274. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  275. alita_sdk-0.3.379.dist-info/RECORD +0 -360
  276. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -6,23 +6,25 @@ from pydantic import BaseModel, Field, create_model
6
6
  import requests
7
7
 
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ....configurations.ado import AdoReposConfiguration
9
+ from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
12
  from .repos_wrapper import ReposApiWrapper
13
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
13
+ from ...utils import check_connection_response
14
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
14
15
 
15
16
  name = "ado_repos"
16
17
 
17
18
 
18
- def _get_toolkit(tool) -> BaseToolkit:
19
+ def get_toolkit(tool) -> BaseToolkit:
19
20
  return AzureDevOpsReposToolkit().get_toolkit(
20
21
  selected_tools=tool['settings'].get('selected_tools', []),
21
- ado_repos_configuration=tool['settings']['ado_repos_configuration'],
22
+ ado_configuration=tool['settings']['ado_configuration'],
23
+ repository_id=tool['settings']['repository_id'],
22
24
  limit=tool['settings'].get('limit', 5),
23
- base_branch=tool['settings'].get('base_branch', ""),
24
- active_branch=tool['settings'].get('active_branch', ""),
25
- toolkit_name=tool['settings'].get('toolkit_name', ""),
25
+ base_branch=tool['settings'].get('base_branch', "main"),
26
+ active_branch=tool['settings'].get('active_branch', "main"),
27
+ toolkit_name=tool.get('toolkit_name', ''),
26
28
  pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
27
29
  embedding_model=tool['settings'].get('embedding_model'),
28
30
  collection_name=tool['toolkit_name'],
@@ -30,26 +32,22 @@ def _get_toolkit(tool) -> BaseToolkit:
30
32
  llm=tool['settings'].get('llm', None),
31
33
  )
32
34
 
33
- def get_toolkit():
34
- return AzureDevOpsReposToolkit.toolkit_config_schema()
35
-
36
35
  def get_tools(tool):
37
- return _get_toolkit(tool).get_tools()
36
+ return get_toolkit(tool).get_tools()
38
37
 
39
38
  class AzureDevOpsReposToolkit(BaseToolkit):
40
39
  tools: List[BaseTool] = []
41
- toolkit_max_length: int = 0
42
40
 
43
41
  @staticmethod
44
42
  def toolkit_config_schema() -> BaseModel:
45
43
  selected_tools = {x['name']: x['args_schema'].schema() for x in ReposApiWrapper.model_construct().get_available_tools()}
46
- AzureDevOpsReposToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
47
44
  m = create_model(
48
45
  name,
49
- ado_repos_configuration=(AdoReposConfiguration, Field(description="Ado Repos configuration", default=None,
50
- json_schema_extra={'configuration_types': ['ado_repos']})),
51
- base_branch=(Optional[str], Field(default="", title="Base branch", description="ADO base branch (e.g., main)")),
52
- active_branch=(Optional[str], Field(default="", title="Active branch", description="ADO active branch (e.g., main)")),
46
+ ado_configuration=(AdoConfiguration, Field(description="ADO configuration", default=None,
47
+ json_schema_extra={'configuration_types': ['ado']})),
48
+ repository_id=(str, Field(description="ADO repository ID or name")),
49
+ base_branch=(Optional[str], Field(default="main", title="Base branch", description="ADO base branch (e.g., main)")),
50
+ active_branch=(Optional[str], Field(default="main", title="Active branch", description="ADO active branch (e.g., main)")),
53
51
 
54
52
  # indexer settings
55
53
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None, description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
@@ -57,20 +55,38 @@ class AzureDevOpsReposToolkit(BaseToolkit):
57
55
  embedding_model=(Optional[str], Field(default=None, description="Embedding configuration.", json_schema_extra={'configuration_model': 'embedding'})),
58
56
 
59
57
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
60
- __config__={'json_schema_extra': {'metadata':
61
- {
62
- "label": "ADO repos",
63
- "icon_url": "ado-repos-icon.svg",
64
- "categories": ["code repositories"],
65
- "extra_categories": ["code", "repository", "version control"],
66
- }}}
58
+ __config__={
59
+ 'json_schema_extra': {
60
+ 'metadata': {
61
+ "label": "ADO repos",
62
+ "icon_url": "ado-repos-icon.svg",
63
+ "categories": ["code repositories"],
64
+ "extra_categories": ["code", "repository", "version control"],
65
+ "sections": {
66
+ "auth": {
67
+ "required": True,
68
+ "subsections": [
69
+ {
70
+ "name": "Token",
71
+ "fields": ["token"]
72
+ }
73
+ ]
74
+ }
75
+ },
76
+ "configuration_group": {
77
+ "name": "ado",
78
+ }
79
+ }
80
+ }
81
+ }
67
82
  )
68
83
 
69
84
  @check_connection_response
70
85
  def check_connection(self):
86
+ ado_config = self.ado_configuration
71
87
  response = requests.get(
72
- f'{self.organization_url}/{self.project}/_apis/git/repositories/{self.repository_id}?api-version=7.0',
73
- headers = {'Authorization': f'Bearer {self.token}'},
88
+ f'{ado_config.organization_url}/{ado_config.project}/_apis/git/repositories/{self.repository_id}?api-version=7.0',
89
+ headers = {'Authorization': f'Bearer {ado_config.token.get_secret_value() if ado_config.token else ""}'},
74
90
  timeout=5
75
91
  )
76
92
  return response
@@ -90,25 +106,28 @@ class AzureDevOpsReposToolkit(BaseToolkit):
90
106
 
91
107
  wrapper_payload = {
92
108
  **kwargs,
93
- # TODO use ado_repos_configuration fields
94
- **kwargs['ado_repos_configuration'],
95
- **kwargs['ado_repos_configuration']['ado_configuration'],
109
+ # Extract ADO configuration fields
110
+ **kwargs['ado_configuration'],
96
111
  **(kwargs.get('pgvector_configuration') or {}),
97
112
  }
98
113
  azure_devops_repos_wrapper = ReposApiWrapper(**wrapper_payload)
99
114
  available_tools = azure_devops_repos_wrapper.get_available_tools()
100
115
  tools = []
101
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
102
116
  for tool in available_tools:
103
117
  if selected_tools:
104
118
  if tool["name"] not in selected_tools:
105
119
  continue
120
+ description = tool["description"] + f"\nADO instance: {azure_devops_repos_wrapper.organization_url}/{azure_devops_repos_wrapper.project}"
121
+ if toolkit_name:
122
+ description = f"{description}\nToolkit: {toolkit_name}"
123
+ description = description[:1000]
106
124
  tools.append(
107
125
  BaseAction(
108
126
  api_wrapper=azure_devops_repos_wrapper,
109
- name=prefix + tool["name"],
110
- description=tool["description"] + f"\nADO instance: {azure_devops_repos_wrapper.organization_url}/{azure_devops_repos_wrapper.project}",
127
+ name=tool["name"],
128
+ description=description,
111
129
  args_schema=tool["args_schema"],
130
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
112
131
  )
113
132
  )
114
133
  return cls(tools=tools)
@@ -23,9 +23,11 @@ from langchain_core.tools import ToolException
23
23
  from msrest.authentication import BasicAuthentication
24
24
  from pydantic import Field, PrivateAttr, create_model, model_validator, SecretStr
25
25
 
26
- from ..utils import extract_old_new_pairs, generate_diff, get_content_from_generator
26
+ from ...elitea_base import BaseCodeToolApiWrapper
27
+ from ..utils import generate_diff, get_content_from_generator
27
28
  from ...code_indexer_toolkit import CodeIndexerToolkit
28
29
  from ...utils.available_tools_decorator import extend_with_parent_available_tools
30
+ from ...utils.tool_prompts import EDIT_FILE_DESCRIPTION, UPDATE_FILE_PROMPT_NO_PATH
29
31
 
30
32
  logger = logging.getLogger(__name__)
31
33
 
@@ -111,8 +113,7 @@ class ArgsSchema(Enum):
111
113
  Field(
112
114
  description=(
113
115
  "Branch to be used for read file operation."
114
- ),
115
- default=None
116
+ )
116
117
  ),
117
118
  )
118
119
  )
@@ -130,9 +131,18 @@ class ArgsSchema(Enum):
130
131
  )
131
132
  UpdateFile = create_model(
132
133
  "UpdateFile",
133
- branch_name=(str, Field(description="The name of the branch, e.g. `my_branch`.")),
134
- file_path=(str, Field(description="Path of a file to be updated.")),
135
- update_query=(str, Field(description="Update query used to adjust target file.")),
134
+ branch_name=(
135
+ str,
136
+ Field(description="The name of the branch, e.g. `my_branch`.")
137
+ ),
138
+ file_path=(
139
+ str,
140
+ Field(description="Path of a file to be updated."),
141
+ ),
142
+ update_query=(
143
+ str,
144
+ Field(description=UPDATE_FILE_PROMPT_NO_PATH),
145
+ ),
136
146
  )
137
147
  DeleteFile = create_model(
138
148
  "DeleteFile",
@@ -244,14 +254,24 @@ class ArgsSchema(Enum):
244
254
 
245
255
 
246
256
  class ReposApiWrapper(CodeIndexerToolkit):
247
- # TODO use ado_repos_configuration fields
248
- organization_url: Optional[str]
249
- project: Optional[str]
250
- repository_id: Optional[str]
251
- base_branch: Optional[str]
252
- active_branch: Optional[str]
253
- token: Optional[SecretStr]
254
- _client: Optional[GitClient] = PrivateAttr()
257
+ # ADO Configuration fields (from AdoConfiguration)
258
+ organization_url: Optional[str] = None
259
+ project: Optional[str] = None
260
+ token: Optional[SecretStr] = None
261
+
262
+ # Repository-specific fields (toolkit level)
263
+ repository_id: Optional[str] = None
264
+ base_branch: Optional[str] = None
265
+ active_branch: Optional[str] = None
266
+
267
+ # Alita instance
268
+ alita: Optional[Any] = None
269
+
270
+ # Client instance - marked as exclude=True
271
+ ado_client_instance: Optional[GitClient] = Field(default=None, exclude=True)
272
+
273
+ # Reuse common file helpers from BaseCodeToolApiWrapper
274
+ edit_file = BaseCodeToolApiWrapper.edit_file
255
275
 
256
276
  class Config:
257
277
  arbitrary_types_allowed = True
@@ -259,43 +279,71 @@ class ReposApiWrapper(CodeIndexerToolkit):
259
279
  @model_validator(mode="before")
260
280
  @classmethod
261
281
  def validate_toolkit(cls, values):
262
- project = values["project"]
263
- organization_url = values["organization_url"]
264
- repository_id = values["repository_id"]
265
- base_branch = values["base_branch"]
266
- active_branch = values["active_branch"]
267
- credentials = BasicAuthentication("", values["token"])
282
+ from langchain.utils import get_from_dict_or_env
283
+
284
+ # Get ADO configuration values
285
+ organization_url = get_from_dict_or_env(values, ["organization_url"], "ADO_ORGANIZATION_URL", default=None)
286
+ project = get_from_dict_or_env(values, ["project"], "ADO_PROJECT", default=None)
287
+ token = get_from_dict_or_env(values, ["token"], "ADO_TOKEN", default=None)
288
+
289
+ # Get repository-specific values
290
+ repository_id = get_from_dict_or_env(values, ["repository_id"], "ADO_REPOSITORY_ID", default=None)
291
+ base_branch = get_from_dict_or_env(values, ["base_branch"], "ADO_BASE_BRANCH", default="main")
292
+ active_branch = get_from_dict_or_env(values, ["active_branch"], "ADO_ACTIVE_BRANCH", default="main")
268
293
 
269
294
  if not organization_url or not project or not repository_id:
270
295
  raise ToolException(
271
296
  "Parameters: organization_url, project, and repository_id are required."
272
297
  )
273
298
 
299
+ credentials = BasicAuthentication("", token)
300
+
274
301
  try:
275
- cls._client = GitClient(base_url=organization_url, creds=credentials)
276
- # workaround to check if user is authorized to access ADO Git
277
- cls._client.get_repository(repository_id, project=project)
302
+ # Initialize ADO Git client
303
+ ado_client = GitClient(base_url=organization_url, creds=credentials)
304
+ # Verify access to repository
305
+ ado_client.get_repository(repository_id, project=project)
306
+
307
+ # Store client instance
308
+ values["ado_client_instance"] = ado_client
309
+
310
+ def branch_exists(branch_name):
311
+ try:
312
+ branch = ado_client.get_branch(
313
+ repository_id=repository_id, name=branch_name, project=project
314
+ )
315
+ return branch is not None
316
+ except Exception:
317
+ return False
318
+
319
+ if base_branch:
320
+ if not branch_exists(base_branch):
321
+ raise ToolException(f"The base branch '{base_branch}' does not exist.")
322
+ if active_branch:
323
+ if not branch_exists(active_branch):
324
+ raise ToolException(f"The active branch '{active_branch}' does not exist.")
325
+
278
326
  except Exception as e:
327
+ if isinstance(e, ToolException):
328
+ raise
279
329
  raise ToolException(f"Failed to connect to Azure DevOps: {e}")
280
330
 
281
- def branch_exists(branch_name):
282
- try:
283
- branch = cls._client.get_branch(
284
- repository_id=repository_id, name=branch_name, project=project
285
- )
286
- return branch is not None
287
- except Exception:
288
- return False
289
-
290
- if base_branch:
291
- if not branch_exists(base_branch):
292
- raise ToolException(f"The base branch '{base_branch}' does not exist.")
293
- if active_branch:
294
- if not branch_exists(active_branch):
295
- raise ToolException(f"The active branch '{active_branch}' does not exist.")
331
+ # Update values with configuration
332
+ values["organization_url"] = organization_url
333
+ values["project"] = project
334
+ values["token"] = token
335
+ values["repository_id"] = repository_id
336
+ values["base_branch"] = base_branch
337
+ values["active_branch"] = active_branch
296
338
 
297
339
  return super().validate_toolkit(values)
298
340
 
341
+ # Expose ADO Git client via property
342
+ @property
343
+ def _client(self) -> GitClient:
344
+ """Access to ADO Git client methods"""
345
+ return self.ado_client_instance
346
+
299
347
  def _get_commits(self, file_path: str, branch: str, top: int = None) -> List[GitCommitRef]:
300
348
  """
301
349
  Get commits for a specific file in a specific branch.
@@ -645,8 +693,18 @@ class ReposApiWrapper(CodeIndexerToolkit):
645
693
 
646
694
  return dumps(data)
647
695
 
648
- def download_file(self, path):
649
- return b"".join(self._client.get_item_content(self.repository_id, path=path, project=self.project, download=True))
696
+ def download_file(self, path: str, branch: str = None):
697
+ branch = branch or self.active_branch or self.base_branch
698
+ version_descriptor = GitVersionDescriptor(
699
+ version=branch, version_type="branch"
700
+ )
701
+ return b"".join(self._client.get_item_content(
702
+ self.repository_id,
703
+ path=path,
704
+ project=self.project,
705
+ download=True,
706
+ version_descriptor=version_descriptor
707
+ ))
650
708
 
651
709
  def get_file_content(self, commit_id, path):
652
710
  version_descriptor = GitVersionDescriptor(
@@ -836,22 +894,50 @@ class ReposApiWrapper(CodeIndexerToolkit):
836
894
  logger.error(msg)
837
895
  return ToolException(msg)
838
896
 
839
- def update_file(self, branch_name: str, file_path: str, update_query: str) -> str:
897
+ def _write_file(self, file_path: str, content: str, branch: str = None, commit_message: str = None) -> str:
898
+ """Write content to a file in Azure DevOps by creating an edit commit.
899
+
900
+ This implementation follows the previous `update_file` behavior: it always
901
+ performs an 'edit' change (does not create the file), gets the latest
902
+ commit id for the branch and pushes a new commit containing the change.
840
903
  """
841
- Updates a file with new content in Azure DevOps.
904
+ try:
905
+ # Get the latest commit ID of the target branch
906
+ branch_obj = self._client.get_branch(
907
+ repository_id=self.repository_id,
908
+ project=self.project,
909
+ name=branch,
910
+ )
911
+ if branch_obj is None or not hasattr(branch_obj, 'commit') or not hasattr(branch_obj.commit, 'commit_id'):
912
+ raise ToolException(f"Branch `{branch}` does not exist or has no commits.")
913
+
914
+ latest_commit_id = branch_obj.commit.commit_id
915
+
916
+ # Build edit change and push
917
+ change = GitChange("edit", file_path, content).to_dict()
918
+
919
+ ref_update = GitRefUpdate(name=f"refs/heads/{branch}", old_object_id=latest_commit_id)
920
+ new_commit = GitCommit(comment=commit_message or ("Update " + file_path), changes=[change])
921
+ push = GitPush(commits=[new_commit], ref_updates=[ref_update])
922
+
923
+ self._client.create_push(push=push, repository_id=self.repository_id, project=self.project)
924
+ return f"Updated file {file_path}"
925
+ except ToolException:
926
+ # Re-raise known tool exceptions
927
+ raise
928
+ except Exception as e:
929
+ logger.error(f"Unable to write file {file_path}: {e}")
930
+ raise ToolException(f"Unable to write file {file_path}: {str(e)}")
931
+
932
+ def update_file(self, branch_name: str, file_path: str, update_query: str) -> str:
933
+ """Updates a file with new content in Azure DevOps using OLD/NEW markers.
934
+
842
935
  Parameters:
843
936
  branch_name (str): The name of the branch where update the file.
844
937
  file_path (str): Path to the file for update.
845
- update_query(str): Contains the file contents requried to be updated.
846
- The old file contents is wrapped in OLD <<<< and >>>> OLD
847
- The new file contents is wrapped in NEW <<<< and >>>> NEW
848
- For example:
849
- OLD <<<<
850
- Hello Earth!
851
- >>>> OLD
852
- NEW <<<<
853
- Hello Mars!
854
- >>>> NEW
938
+ update_query(str): Contains the file contents required to be updated,
939
+ wrapped in Git-style OLD/NEW blocks.
940
+
855
941
  Returns:
856
942
  A success or failure message
857
943
  """
@@ -864,43 +950,16 @@ class ReposApiWrapper(CodeIndexerToolkit):
864
950
  "Please create a new branch and try again."
865
951
  )
866
952
  try:
867
- file_content = self._read_file(file_path, branch_name)
868
-
869
- if isinstance(file_content, ToolException):
870
- return file_content
871
-
872
- updated_file_content = file_content
873
- for old, new in extract_old_new_pairs(update_query):
874
- if not old.strip():
875
- continue
876
- updated_file_content = updated_file_content.replace(old, new)
877
-
878
- if file_content == updated_file_content:
879
- return (
880
- "File content was not updated because old content was not found or empty. "
881
- "It may be helpful to use the read_file action to get "
882
- "the current file contents."
883
- )
884
-
885
- # Get the latest commit ID of the active branch to use as oldObjectId
886
- branch = self._client.get_branch(
887
- repository_id=self.repository_id,
888
- project=self.project,
889
- name=self.active_branch,
890
- )
891
- latest_commit_id = branch.commit.commit_id
892
-
893
- change = GitChange("edit", file_path, updated_file_content).to_dict()
894
-
895
- ref_update = GitRefUpdate(
896
- name=f"refs/heads/{self.active_branch}", old_object_id=latest_commit_id
897
- )
898
- new_commit = GitCommit(comment=f"Update {file_path}", changes=[change])
899
- push = GitPush(commits=[new_commit], ref_updates=[ref_update])
900
- self._client.create_push(
901
- push=push, repository_id=self.repository_id, project=self.project
953
+ # Let edit_file handle parsing and content updates; this will call _read_file and _write_file.
954
+ # For ADO, branch_name is used as branch; commit message is derived from file_path.
955
+ return self.edit_file(
956
+ file_path=file_path,
957
+ file_query=update_query,
958
+ branch=self.active_branch,
959
+ commit_message=f"Update {file_path}",
902
960
  )
903
- return "Updated file " + file_path
961
+ except ToolException as e:
962
+ return str(e)
904
963
  except Exception as e:
905
964
  msg = f"Unable to update file due to error:\n{str(e)}"
906
965
  logger.error(msg)
@@ -1236,7 +1295,7 @@ class ReposApiWrapper(CodeIndexerToolkit):
1236
1295
  {
1237
1296
  "ref": self.update_file,
1238
1297
  "name": "update_file",
1239
- "description": self.update_file.__doc__,
1298
+ "description": EDIT_FILE_DESCRIPTION,
1240
1299
  "args_schema": ArgsSchema.UpdateFile.value,
1241
1300
  },
1242
1301
  {
@@ -10,26 +10,38 @@ from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from .test_plan_wrapper import TestPlanApiWrapper
12
12
  from ...base.tool import BaseAction
13
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
13
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
14
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
14
15
 
15
16
 
16
17
  name = "azure_devops_plans"
17
18
  name_alias = "ado_plans"
18
19
 
20
+ def get_toolkit(tool):
21
+ return AzureDevOpsPlansToolkit().get_toolkit(
22
+ selected_tools=tool['settings'].get('selected_tools', []),
23
+ ado_configuration=tool['settings']['ado_configuration'],
24
+ limit=tool['settings'].get('limit', 5),
25
+ toolkit_name=tool.get('toolkit_name', ''),
26
+ alita=tool['settings'].get('alita', None),
27
+ llm=tool['settings'].get('llm', None),
28
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
29
+ collection_name=tool['toolkit_name'],
30
+ doctype='doc',
31
+ embedding_model=tool['settings'].get('embedding_model'),
32
+ vectorstore_type="PGVector"
33
+ )
19
34
 
20
35
  class AzureDevOpsPlansToolkit(BaseToolkit):
21
36
  tools: List[BaseTool] = []
22
- toolkit_max_length: int = 0
23
37
 
24
38
  @staticmethod
25
39
  def toolkit_config_schema() -> BaseModel:
26
40
  selected_tools = {x['name']: x['args_schema'].schema() for x in TestPlanApiWrapper.model_construct().get_available_tools()}
27
- AzureDevOpsPlansToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
28
41
  m = create_model(
29
42
  name_alias,
30
- name=(str, Field(description="Toolkit name", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsPlansToolkit.toolkit_max_length})),
31
43
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
32
- limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
44
+ limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5, gt=0)),
33
45
  # indexer settings
34
46
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None,
35
47
  description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
@@ -97,17 +109,21 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
97
109
  azure_devops_api_wrapper = TestPlanApiWrapper(**wrapper_payload)
98
110
  available_tools = azure_devops_api_wrapper.get_available_tools()
99
111
  tools = []
100
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
101
112
  for tool in available_tools:
102
113
  if selected_tools:
103
114
  if tool["name"] not in selected_tools:
104
115
  continue
105
116
  print(tool)
117
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}"
118
+ if toolkit_name:
119
+ description = f"{description}\nToolkit: {toolkit_name}"
120
+ description = description[:1000]
106
121
  tools.append(BaseAction(
107
122
  api_wrapper=azure_devops_api_wrapper,
108
- name=prefix + tool["name"],
109
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}",
110
- args_schema=tool["args_schema"]
123
+ name=tool["name"],
124
+ description=description,
125
+ args_schema=tool["args_schema"],
126
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
111
127
  ))
112
128
  return cls(tools=tools)
113
129
 
@@ -180,7 +180,29 @@ class TestPlanApiWrapper(NonCodeIndexerToolkit):
180
180
  connection = Connection(base_url=values['organization_url'], creds=credentials)
181
181
  cls._client = connection.clients.get_test_plan_client()
182
182
  except Exception as e:
183
- raise ImportError(f"Failed to connect to Azure DevOps: {e}")
183
+ error_msg = str(e).lower()
184
+ if "expired" in error_msg or "token" in error_msg and ("invalid" in error_msg or "unauthorized" in error_msg):
185
+ raise ValueError(
186
+ "Azure DevOps connection failed: Your access token has expired or is invalid. "
187
+ "Please refresh your token in the toolkit configuration."
188
+ )
189
+ elif "401" in error_msg or "unauthorized" in error_msg:
190
+ raise ValueError(
191
+ "Azure DevOps connection failed: Authentication failed. "
192
+ "Please check your credentials in the toolkit configuration."
193
+ )
194
+ elif "404" in error_msg or "not found" in error_msg:
195
+ raise ValueError(
196
+ "Azure DevOps connection failed: Organization or project not found. "
197
+ "Please verify your organization URL and project name."
198
+ )
199
+ elif "timeout" in error_msg or "timed out" in error_msg:
200
+ raise ValueError(
201
+ "Azure DevOps connection failed: Connection timed out. "
202
+ "Please check your network connection and try again."
203
+ )
204
+ else:
205
+ raise ValueError(f"Azure DevOps connection failed: {e}")
184
206
  return super().validate_toolkit(values)
185
207
 
186
208
  def create_test_plan(self, test_plan_create_params: str):
@@ -1,24 +1,6 @@
1
- import re
2
1
  import difflib
3
2
 
4
3
 
5
- def extract_old_new_pairs(file_query: str):
6
- """
7
- Extracts old and new content pairs from a file query.
8
- Parameters:
9
- file_query (str): The file query containing old and new content.
10
- Returns:
11
- list of tuples: A list where each tuple contains (old_content, new_content).
12
- """
13
- old_pattern = re.compile(r"OLD <<<<\s*(.*?)\s*>>>> OLD", re.DOTALL)
14
- new_pattern = re.compile(r"NEW <<<<\s*(.*?)\s*>>>> NEW", re.DOTALL)
15
-
16
- old_contents = old_pattern.findall(file_query)
17
- new_contents = new_pattern.findall(file_query)
18
-
19
- return list(zip(old_contents, new_contents))
20
-
21
-
22
4
  def generate_diff(base_text, target_text, file_path):
23
5
  base_lines = base_text.splitlines(keepends=True)
24
6
  target_lines = target_text.splitlines(keepends=True)
@@ -28,6 +10,7 @@ def generate_diff(base_text, target_text, file_path):
28
10
 
29
11
  return "".join(diff)
30
12
 
13
+
31
14
  def get_content_from_generator(content_generator):
32
15
  def safe_decode(chunk):
33
16
  try: