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,42 @@ 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 .test_plan_wrapper import TestPlanApiWrapper
10
12
  from ...base.tool import BaseAction
11
- 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
12
15
 
13
16
 
14
17
  name = "azure_devops_plans"
15
18
  name_alias = "ado_plans"
16
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
+ )
17
34
 
18
35
  class AzureDevOpsPlansToolkit(BaseToolkit):
19
36
  tools: List[BaseTool] = []
20
- toolkit_max_length: int = 0
21
37
 
22
38
  @staticmethod
23
39
  def toolkit_config_schema() -> BaseModel:
24
40
  selected_tools = {x['name']: x['args_schema'].schema() for x in TestPlanApiWrapper.model_construct().get_available_tools()}
25
- AzureDevOpsPlansToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
26
41
  m = create_model(
27
42
  name_alias,
28
- name=(str, Field(description="Toolkit name", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AzureDevOpsPlansToolkit.toolkit_max_length})),
29
43
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
30
44
  limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
31
45
  # indexer settings
@@ -79,6 +93,7 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
79
93
  return m
80
94
 
81
95
  @classmethod
96
+ @filter_missconfigured_index_tools
82
97
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
83
98
  from os import environ
84
99
  if not environ.get('AZURE_DEVOPS_CACHE_DIR', None):
@@ -94,17 +109,21 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
94
109
  azure_devops_api_wrapper = TestPlanApiWrapper(**wrapper_payload)
95
110
  available_tools = azure_devops_api_wrapper.get_available_tools()
96
111
  tools = []
97
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
98
112
  for tool in available_tools:
99
113
  if selected_tools:
100
114
  if tool["name"] not in selected_tools:
101
115
  continue
102
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]
103
121
  tools.append(BaseAction(
104
122
  api_wrapper=azure_devops_api_wrapper,
105
- name=prefix + tool["name"],
106
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}",
107
- 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"]}
108
127
  ))
109
128
  return cls(tools=tools)
110
129
 
@@ -1,7 +1,8 @@
1
1
  import json
2
2
  import logging
3
+ import re
3
4
  import xml.etree.ElementTree as ET
4
- from typing import Generator, Optional
5
+ from typing import Generator, Literal, Optional, List
5
6
 
6
7
  from azure.devops.connection import Connection
7
8
  from azure.devops.v7_0.test_plan.models import TestPlanCreateParams, TestSuiteCreateParams, \
@@ -14,12 +15,9 @@ from pydantic import create_model, PrivateAttr, model_validator, SecretStr
14
15
  from pydantic.fields import FieldInfo as Field
15
16
 
16
17
  from ..work_item import AzureDevOpsApiWrapper
17
- from ...elitea_base import BaseVectorStoreToolApiWrapper, extend_with_vector_tools
18
-
19
- try:
20
- from alita_sdk.runtime.langchain.interfaces.llm_processor import get_embeddings
21
- except ImportError:
22
- from alita_sdk.langchain.interfaces.llm_processor import get_embeddings
18
+ from ...non_code_indexer_toolkit import NonCodeIndexerToolkit
19
+ from ...utils.available_tools_decorator import extend_with_parent_available_tools
20
+ from ....runtime.utils.utils import IndexerKeywords
23
21
 
24
22
  logger = logging.getLogger(__name__)
25
23
 
@@ -163,7 +161,7 @@ TestCasesGetModel = create_model(
163
161
  suite_id=(int, Field(description="ID of the test suite for which test cases are requested"))
164
162
  )
165
163
 
166
- class TestPlanApiWrapper(BaseVectorStoreToolApiWrapper):
164
+ class TestPlanApiWrapper(NonCodeIndexerToolkit):
167
165
  # TODO use ado_configuration instead of organization_url, project and token
168
166
  __test__ = False
169
167
  organization_url: str
@@ -183,7 +181,7 @@ class TestPlanApiWrapper(BaseVectorStoreToolApiWrapper):
183
181
  cls._client = connection.clients.get_test_plan_client()
184
182
  except Exception as e:
185
183
  raise ImportError(f"Failed to connect to Azure DevOps: {e}")
186
- return values
184
+ return super().validate_toolkit(values)
187
185
 
188
186
  def create_test_plan(self, test_plan_create_params: str):
189
187
  """Create a test plan in Azure DevOps."""
@@ -280,7 +278,7 @@ class TestPlanApiWrapper(BaseVectorStoreToolApiWrapper):
280
278
  test_steps_format: str = 'json'):
281
279
  """Creates a new test case in specified suite in Azure DevOps."""
282
280
  work_item_wrapper = AzureDevOpsApiWrapper(organization_url=self.organization_url,
283
- token=self.token.get_secret_value(), project=self.project)
281
+ token=self.token.get_secret_value(), project=self.project, llm=self.llm)
284
282
  if test_steps_format == 'json':
285
283
  steps_xml = self.get_test_steps_xml(json.loads(test_steps))
286
284
  elif test_steps_format == 'xml':
@@ -362,40 +360,70 @@ class TestPlanApiWrapper(BaseVectorStoreToolApiWrapper):
362
360
  test_cases = self._client.get_test_case_list(self.project, plan_id, suite_id)
363
361
  return [test_case.as_dict() for test_case in test_cases]
364
362
  except Exception as e:
363
+ self._log_tool_event(f"Error getting test cases: {e}", 'get_test_cases')
365
364
  logger.error(f"Error getting test cases: {e}")
366
365
  return ToolException(f"Error getting test cases: {e}")
367
366
 
368
- def _base_loader(self, plan_id: str, suite_ids: Optional[list[str]] = [], **kwargs) -> Generator[Document, None, None]:
367
+ def get_suites_in_plan(self, plan_id: int) -> List[dict]:
368
+ """Get all test suites in a test plan."""
369
+ try:
370
+ test_suites = self._client.get_test_suites_for_plan(self.project, plan_id)
371
+ return [suite.as_dict() for suite in test_suites]
372
+ except Exception as e:
373
+ logger.error(f"Error getting test suites: {e}")
374
+ return ToolException(f"Error getting test suites: {e}")
375
+
376
+ def _base_loader(self, plan_id: int, suite_ids: Optional[List[int]] = [], chunking_tool: str = None, **kwargs) -> Generator[Document, None, None]:
369
377
  cases = []
378
+ if not suite_ids:
379
+ suites = self.get_suites_in_plan(plan_id)
380
+ suite_ids = [suite['id'] for suite in suites if 'id' in suite]
370
381
  for sid in suite_ids:
371
382
  cases.extend(self.get_test_cases(plan_id, sid))
372
383
  #
373
384
  for case in cases:
374
385
  field_dicts = case.get('work_item', {}).get('work_item_fields', [])
375
386
  data = {k: v for d in field_dicts for k, v in d.items()}
376
- yield Document(
377
- page_content=data.get('Microsoft.VSTS.TCM.Steps', ''),
378
- metadata={
379
- 'id': case.get('work_item', {}).get('id', ''),
380
- 'title': case.get('work_item', {}).get('name', ''),
381
- 'plan_id': case.get('test_plan', {}).get('id', ''),
382
- 'suite_id': case.get('test_suite', {}).get('id', ''),
383
- 'description': data.get('System.Description', ''),
384
- 'updated_on': data.get('System.Rev', ''),
385
- })
386
-
387
- def _process_document(self, document: Document) -> Generator[Document, None, None]:
388
- if False:
389
- yield # Unreachable, but keeps the function a generator
387
+ if chunking_tool:
388
+ steps = data.get('Microsoft.VSTS.TCM.Steps', '')
389
+ # Remove XML declaration if present (like <?xml version="1.0" encoding="utf-16"?>) to avoid encoding issues
390
+ steps_no_decl = re.sub(r'<\?xml[^>]*\?>', '', steps, count=1).lstrip()
391
+
392
+ yield Document(
393
+ page_content='',
394
+ metadata={
395
+ 'id': case.get('work_item', {}).get('id', ''),
396
+ 'title': case.get('work_item', {}).get('name', ''),
397
+ 'plan_id': case.get('test_plan', {}).get('id', ''),
398
+ 'suite_id': case.get('test_suite', {}).get('id', ''),
399
+ 'description': data.get('System.Description', ''),
400
+ 'updated_on': data.get('System.Rev', ''),
401
+ # content is in metadata for chunking tool post-processing
402
+ IndexerKeywords.CONTENT_IN_BYTES.value: steps_no_decl.encode("utf-8")
403
+ })
404
+ else:
405
+ yield Document(
406
+ page_content=data.get('Microsoft.VSTS.TCM.Steps', ''),
407
+ metadata={
408
+ 'id': case.get('work_item', {}).get('id', ''),
409
+ 'title': case.get('work_item', {}).get('name', ''),
410
+ 'plan_id': case.get('test_plan', {}).get('id', ''),
411
+ 'suite_id': case.get('test_suite', {}).get('id', ''),
412
+ 'description': data.get('System.Description', ''),
413
+ 'updated_on': data.get('System.Rev', ''),
414
+ })
390
415
 
391
416
  def _index_tool_params(self):
392
417
  """Return the parameters for indexing data."""
393
418
  return {
394
- "plan_id": (str, Field(description="ID of the test plan for which test cases are requested")),
395
- "suite_ids": (str, Field(description="List of test suite IDs for which test cases are requested (can be empty)"))
419
+ 'chunking_tool': (Literal['xml', ''], Field(description="Name of chunking tool", default='xml')),
420
+ "plan_id": (int, Field(description="ID of the test plan for which test cases are requested")),
421
+ "suite_ids": (Optional[List[int]], Field(description='List of test suite IDs for which test cases are requested '
422
+ '(can be empty for all suites indexing from the plan). '
423
+ 'Example: [2, 23]', default=[])),
396
424
  }
397
425
 
398
- @extend_with_vector_tools
426
+ @extend_with_parent_available_tools
399
427
  def get_available_tools(self):
400
428
  """Return a list of available tools."""
401
429
  return [
@@ -4,29 +4,40 @@ 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 = "azure_devops_wiki"
13
16
  name_alias = 'ado_wiki'
14
17
 
18
+ def get_toolkit(tool):
19
+ return AzureDevOpsWikiToolkit().get_toolkit(
20
+ selected_tools=tool['settings'].get('selected_tools', []),
21
+ ado_configuration=tool['settings']['ado_configuration'],
22
+ limit=tool['settings'].get('limit', 5),
23
+ toolkit_name=tool.get('toolkit_name', ''),
24
+ alita=tool['settings'].get('alita', None),
25
+ llm=tool['settings'].get('llm', None),
26
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
27
+ collection_name=tool['toolkit_name'],
28
+ doctype='doc',
29
+ embedding_model=tool['settings'].get('embedding_model'),
30
+ vectorstore_type="PGVector"
31
+ )
32
+
15
33
  class AzureDevOpsWikiToolkit(BaseToolkit):
16
34
  tools: List[BaseTool] = []
17
- toolkit_max_length: int = 0
18
35
 
19
36
  @staticmethod
20
37
  def toolkit_config_schema() -> BaseModel:
21
38
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
22
- AzureDevOpsWikiToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
23
39
  m = create_model(
24
40
  name_alias,
25
- name=(str, Field(description="Toolkit name",
26
- json_schema_extra={
27
- 'toolkit_name': True,
28
- 'max_toolkit_length': AzureDevOpsWikiToolkit.toolkit_max_length})
29
- ),
30
41
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
31
42
  # indexer settings
32
43
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None,
@@ -77,6 +88,7 @@ class AzureDevOpsWikiToolkit(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):
@@ -92,16 +104,20 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
92
104
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
93
105
  available_tools = azure_devops_api_wrapper.get_available_tools()
94
106
  tools = []
95
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
96
107
  for tool in available_tools:
97
108
  if selected_tools:
98
109
  if tool["name"] not in selected_tools:
99
110
  continue
111
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
112
+ if toolkit_name:
113
+ description = f"{description}\nToolkit: {toolkit_name}"
114
+ description = description[:1000]
100
115
  tools.append(BaseAction(
101
116
  api_wrapper=azure_devops_api_wrapper,
102
- name=prefix + tool["name"],
103
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
104
- args_schema=tool["args_schema"]
117
+ name=tool["name"],
118
+ description=description,
119
+ args_schema=tool["args_schema"],
120
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
105
121
  ))
106
122
  return cls(tools=tools)
107
123
 
@@ -1,6 +1,8 @@
1
1
  import hashlib
2
2
  import logging
3
- from typing import Any, Dict, Generator, Optional
3
+ import re
4
+ import requests
5
+ from typing import Generator, Literal, Optional
4
6
 
5
7
  from azure.devops.connection import Connection
6
8
  from azure.devops.exceptions import AzureDevOpsServiceError
@@ -15,12 +17,12 @@ from pydantic import create_model, PrivateAttr, SecretStr
15
17
  from pydantic import model_validator
16
18
  from pydantic.fields import Field
17
19
 
18
- from ...elitea_base import BaseVectorStoreToolApiWrapper, extend_with_vector_tools
19
-
20
- try:
21
- from alita_sdk.runtime.langchain.interfaces.llm_processor import get_embeddings
22
- except ImportError:
23
- from alita_sdk.langchain.interfaces.llm_processor import get_embeddings
20
+ import alita_sdk.tools.ado.work_item
21
+ from ..repos import ReposApiWrapper
22
+ from ...non_code_indexer_toolkit import NonCodeIndexerToolkit
23
+ from ...utils.available_tools_decorator import extend_with_parent_available_tools
24
+ from ...utils.content_parser import parse_file_content
25
+ from ....runtime.utils.utils import IndexerKeywords
24
26
 
25
27
  logger = logging.getLogger(__name__)
26
28
 
@@ -32,13 +34,17 @@ GetWikiInput = create_model(
32
34
  GetPageByPathInput = create_model(
33
35
  "GetPageByPathInput",
34
36
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
35
- page_name=(str, Field(description="Wiki page path"))
37
+ page_name=(str, Field(description="Wiki page path")),
38
+ image_description_prompt=(Optional[str],
39
+ Field(description="Prompt which is used for image description", default=None))
36
40
  )
37
41
 
38
42
  GetPageByIdInput = create_model(
39
43
  "GetPageByIdInput",
40
44
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
41
- page_id=(int, Field(description="Wiki page ID"))
45
+ page_id=(int, Field(description="Wiki page ID")),
46
+ image_description_prompt=(Optional[str],
47
+ Field(description="Prompt which is used for image description", default=None))
42
48
  )
43
49
 
44
50
  ModifyPageInput = create_model(
@@ -46,8 +52,9 @@ ModifyPageInput = create_model(
46
52
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
47
53
  page_name=(str, Field(description="Wiki page name")),
48
54
  page_content=(str, Field(description="Wiki page content")),
49
- version_identifier=(str, Field(description="Version string identifier (name of tag/branch, SHA1 of commit)")),
50
- version_type=(Optional[str], Field(description="Version type (branch, tag, or commit). Determines how Id is interpreted", default="branch"))
55
+ version_identifier=(str, Field(description="Version string identifier (name of tag/branch, SHA1 of commit). Usually for wiki the branch is 'wikiMaster'")),
56
+ version_type=(Optional[str], Field(description="Version type (branch, tag, or commit). Determines how Id is interpreted", default="branch")),
57
+ expanded=(Optional[bool], Field(description="Whether to return the full page object or just its simplified version.", default=False))
51
58
  )
52
59
 
53
60
  RenamePageInput = create_model(
@@ -60,7 +67,20 @@ RenamePageInput = create_model(
60
67
  )
61
68
 
62
69
 
63
- class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
70
+ def _format_wiki_page_response(wiki_page_response, expanded: bool = False):
71
+ """Format wiki page response."""
72
+ try:
73
+ return {
74
+ 'eTag': wiki_page_response.eTag,
75
+ 'page': wiki_page_response.page.__dict__ if wiki_page_response.page else None
76
+ } if expanded else {"eTag": wiki_page_response.eTag, "id": wiki_page_response.page.id,
77
+ "page": wiki_page_response.page.url}
78
+ except:
79
+ logger.error(f"Unable to format wiki page response: {wiki_page_response}")
80
+ return wiki_page_response
81
+
82
+
83
+ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
64
84
  # TODO use ado_configuration instead of organization_url, project and token
65
85
  organization_url: str
66
86
  project: str
@@ -68,13 +88,6 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
68
88
  _client: Optional[WikiClient] = PrivateAttr() # Private attribute for the wiki client
69
89
  _core_client: Optional[CoreClient] = PrivateAttr() # Private attribute for the CoreClient client
70
90
 
71
- llm: Any = None
72
- connection_string: Optional[SecretStr] = None
73
- collection_name: Optional[str] = None
74
- embedding_model: Optional[str] = "HuggingFaceEmbeddings"
75
- embedding_model_params: Optional[Dict[str, Any]] = {"model_name": "sentence-transformers/all-MiniLM-L6-v2"}
76
- vectorstore_type: Optional[str] = "PGVector"
77
-
78
91
  class Config:
79
92
  arbitrary_types_allowed = True # Allow arbitrary types (e.g., WorkItemTrackingClient)
80
93
 
@@ -94,7 +107,7 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
94
107
  except Exception as e:
95
108
  return ImportError(f"Failed to connect to Azure DevOps: {e}")
96
109
 
97
- return values
110
+ return super().validate_toolkit(values)
98
111
 
99
112
  def get_wiki(self, wiki_identified: str):
100
113
  """Extract ADO wiki information."""
@@ -104,24 +117,75 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
104
117
  logger.error(f"Error during the attempt to extract wiki: {str(e)}")
105
118
  return ToolException(f"Error during the attempt to extract wiki: {str(e)}")
106
119
 
107
- def get_wiki_page_by_path(self, wiki_identified: str, page_name: str):
120
+ def get_wiki_page_by_path(self, wiki_identified: str, page_name: str, image_description_prompt=None):
108
121
  """Extract ADO wiki page content."""
109
122
  try:
110
- return self._client.get_page(project=self.project, wiki_identifier=wiki_identified, path=page_name,
111
- include_content=True).page.content
123
+ return self._process_images(self._client.get_page(project=self.project, wiki_identifier=wiki_identified, path=page_name,
124
+ include_content=True).page.content,
125
+ image_description_prompt=image_description_prompt)
112
126
  except Exception as e:
113
127
  logger.error(f"Error during the attempt to extract wiki page: {str(e)}")
114
128
  return ToolException(f"Error during the attempt to extract wiki page: {str(e)}")
115
129
 
116
- def get_wiki_page_by_id(self, wiki_identified: str, page_id: int):
130
+ def get_wiki_page_by_id(self, wiki_identified: str, page_id: int, image_description_prompt=None):
117
131
  """Extract ADO wiki page content."""
118
132
  try:
119
- return (self._client.get_page_by_id(project=self.project, wiki_identifier=wiki_identified, id=page_id,
120
- include_content=True).page.content)
133
+ return self._process_images(self._client.get_page_by_id(project=self.project, wiki_identifier=wiki_identified, id=page_id,
134
+ include_content=True).page.content,
135
+ image_description_prompt=image_description_prompt)
121
136
  except Exception as e:
122
137
  logger.error(f"Error during the attempt to extract wiki page: {str(e)}")
123
138
  return ToolException(f"Error during the attempt to extract wiki page: {str(e)}")
124
139
 
140
+ def _process_images(self, page_content: str, image_description_prompt=None):
141
+
142
+ image_pattern = r"!\[(.*?)\]\((.*?)\)"
143
+ matches = re.findall(image_pattern, page_content)
144
+
145
+ for image_name, image_url in matches:
146
+ if image_url.startswith("/.attachments/"):
147
+ try:
148
+ description = self.process_attachment(attachment_url=image_url,
149
+ attachment_name=image_name,
150
+ image_description_prompt=image_description_prompt)
151
+ except Exception as e:
152
+ logger.error(f"Error parsing attachment: {str(e)}")
153
+ description = f"Error parsing attachment: {image_url}"
154
+ else:
155
+ try:
156
+ response = requests.get(image_url)
157
+ response.raise_for_status()
158
+ file_content = response.content
159
+ description = parse_file_content(
160
+ file_content=file_content,
161
+ file_name="image.png",
162
+ llm=self.llm,
163
+ prompt=image_description_prompt
164
+ )
165
+ except Exception as e:
166
+ logger.error(f"Error fetching external image: {str(e)}")
167
+ description = f"Error fetching external image: image_url"
168
+
169
+ new_image_markdown = f"![{image_name}]({description})"
170
+ page_content = page_content.replace(f"![{image_name}]({image_url})", new_image_markdown)
171
+ return page_content
172
+
173
+ def process_attachment(self, attachment_url, attachment_name, image_description_prompt):
174
+ wiki_master_branch = "wikiMaster"
175
+ repos_wrapper = ReposApiWrapper(organization_url=self.organization_url,
176
+ project=self.project,
177
+ token=self.token.get_secret_value(),
178
+ repository_id="Test_agent.wiki",
179
+ base_branch=wiki_master_branch,
180
+ active_branch=wiki_master_branch)
181
+ attachment_content = repos_wrapper.download_file(path=attachment_url)
182
+ return parse_file_content(
183
+ file_content=attachment_content,
184
+ file_name=attachment_name,
185
+ llm=self.llm,
186
+ prompt=image_description_prompt
187
+ )
188
+
125
189
  def delete_page_by_path(self, wiki_identified: str, page_name: str):
126
190
  """Extract ADO wiki page content."""
127
191
  try:
@@ -176,7 +240,7 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
176
240
  logger.error(f"Unable to rename wiki page: {str(e)}")
177
241
  return ToolException(f"Unable to rename wiki page: {str(e)}")
178
242
 
179
- def modify_wiki_page(self, wiki_identified: str, page_name: str, page_content: str, version_identifier: str, version_type: str = "branch"):
243
+ def modify_wiki_page(self, wiki_identified: str, page_name: str, page_content: str, version_identifier: str, version_type: str = "branch", expanded: Optional[bool] = False):
180
244
  """Create or Update ADO wiki page content."""
181
245
  try:
182
246
  all_wikis = [wiki.name for wiki in self._client.get_all_wikis(project=self.project)]
@@ -207,31 +271,31 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
207
271
  return ToolException(f"Unable to extract page by path {page_name}: {str(get_page_e)}")
208
272
 
209
273
  try:
210
- return self._client.create_or_update_page(
274
+ return _format_wiki_page_response(self._client.create_or_update_page(
211
275
  project=self.project,
212
276
  wiki_identifier=wiki_identified,
213
277
  path=page_name,
214
278
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
215
279
  version=version,
216
280
  version_descriptor=GitVersionDescriptor(version=version_identifier, version_type=version_type)
217
- )
281
+ ), expanded=expanded)
218
282
  except AzureDevOpsServiceError as e:
219
283
  if "The version '{0}' either is invalid or does not exist." in str(e):
220
284
  # Retry the request without version_descriptor
221
- return self._client.create_or_update_page(
285
+ return _format_wiki_page_response(wiki_page_response=self._client.create_or_update_page(
222
286
  project=self.project,
223
287
  wiki_identifier=wiki_identified,
224
288
  path=page_name,
225
289
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
226
290
  version=version
227
- )
291
+ ), expanded=expanded)
228
292
  else:
229
293
  raise
230
294
  except Exception as e:
231
295
  logger.error(f"Unable to modify wiki page: {str(e)}")
232
296
  return ToolException(f"Unable to modify wiki page: {str(e)}")
233
297
 
234
- def _base_loader(self, wiki_identifier: str, title_contains: Optional[str] = None, **kwargs) -> Generator[Document, None, None]:
298
+ def _base_loader(self, wiki_identifier: str, chunking_tool: str = None, title_contains: Optional[str] = None, **kwargs) -> Generator[Document, None, None]:
235
299
  pages = self._client.get_pages_batch(pages_batch_request={}, project=self.project, wiki_identifier=wiki_identifier)
236
300
  #
237
301
  for page in pages:
@@ -239,21 +303,31 @@ class AzureDevOpsApiWrapper(BaseVectorStoreToolApiWrapper):
239
303
  content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()
240
304
  title = page.path.rsplit("/", 1)[-1]
241
305
  if not title_contains or (title_contains and title_contains.lower() in title.lower()):
242
- yield Document(page_content=content, metadata={
243
- 'id': str(page.id),
244
- 'path': page.path,
245
- 'title': title,
246
- 'updated_on': content_hash
247
- })
306
+ if chunking_tool:
307
+ yield Document(page_content='', metadata={
308
+ 'id': str(page.id),
309
+ 'path': page.path,
310
+ 'title': title,
311
+ 'updated_on': content_hash,
312
+ IndexerKeywords.CONTENT_IN_BYTES.value: content.encode("utf-8")
313
+ })
314
+ else:
315
+ yield Document(page_content=content, metadata={
316
+ 'id': str(page.id),
317
+ 'path': page.path,
318
+ 'title': title,
319
+ 'updated_on': content_hash
320
+ })
248
321
 
249
322
  def _index_tool_params(self):
250
323
  """Return the parameters for indexing data."""
251
324
  return {
325
+ 'chunking_tool': (Literal['markdown', ''], Field(description="Name of chunking tool", default='markdown')),
252
326
  "wiki_identifier": (str, Field(description="Wiki identifier to index, e.g., 'ABCProject.wiki'")),
253
- 'title_contains': (Optional[str], Field(default=None, description="Optional filter to include only pages with titles containing exact this string"))
327
+ 'title_contains': (Optional[str], Field(default=None, description="Optional filter to include only pages with titles containing exact this string")),
254
328
  }
255
329
 
256
- @extend_with_vector_tools
330
+ @extend_with_parent_available_tools
257
331
  def get_available_tools(self):
258
332
  """Return a list of available tools."""
259
333
  return [