alita-sdk 0.3.351__py3-none-any.whl → 0.3.499__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 (206) 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 +3601 -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 +1256 -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 +64 -8
  30. alita_sdk/community/inventory/__init__.py +224 -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/visualize.py +1370 -0
  58. alita_sdk/configurations/bitbucket.py +94 -2
  59. alita_sdk/configurations/confluence.py +96 -1
  60. alita_sdk/configurations/gitlab.py +79 -0
  61. alita_sdk/configurations/jira.py +103 -0
  62. alita_sdk/configurations/testrail.py +88 -0
  63. alita_sdk/configurations/xray.py +93 -0
  64. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  65. alita_sdk/configurations/zephyr_essential.py +75 -0
  66. alita_sdk/runtime/clients/artifact.py +1 -1
  67. alita_sdk/runtime/clients/client.py +214 -42
  68. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  69. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  70. alita_sdk/runtime/clients/sandbox_client.py +373 -0
  71. alita_sdk/runtime/langchain/assistant.py +118 -30
  72. alita_sdk/runtime/langchain/constants.py +8 -1
  73. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  74. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  75. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -1
  76. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +41 -12
  77. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -1
  78. alita_sdk/runtime/langchain/document_loaders/constants.py +116 -99
  79. alita_sdk/runtime/langchain/interfaces/llm_processor.py +2 -2
  80. alita_sdk/runtime/langchain/langraph_agent.py +307 -71
  81. alita_sdk/runtime/langchain/utils.py +48 -8
  82. alita_sdk/runtime/llms/preloaded.py +2 -6
  83. alita_sdk/runtime/models/mcp_models.py +61 -0
  84. alita_sdk/runtime/toolkits/__init__.py +26 -0
  85. alita_sdk/runtime/toolkits/application.py +9 -2
  86. alita_sdk/runtime/toolkits/artifact.py +18 -6
  87. alita_sdk/runtime/toolkits/datasource.py +13 -6
  88. alita_sdk/runtime/toolkits/mcp.py +780 -0
  89. alita_sdk/runtime/toolkits/planning.py +178 -0
  90. alita_sdk/runtime/toolkits/tools.py +205 -55
  91. alita_sdk/runtime/toolkits/vectorstore.py +9 -4
  92. alita_sdk/runtime/tools/__init__.py +11 -3
  93. alita_sdk/runtime/tools/application.py +7 -0
  94. alita_sdk/runtime/tools/artifact.py +225 -12
  95. alita_sdk/runtime/tools/function.py +95 -5
  96. alita_sdk/runtime/tools/graph.py +10 -4
  97. alita_sdk/runtime/tools/image_generation.py +212 -0
  98. alita_sdk/runtime/tools/llm.py +494 -102
  99. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  100. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  101. alita_sdk/runtime/tools/mcp_server_tool.py +4 -4
  102. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  103. alita_sdk/runtime/tools/planning/models.py +246 -0
  104. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  105. alita_sdk/runtime/tools/router.py +2 -1
  106. alita_sdk/runtime/tools/sandbox.py +180 -79
  107. alita_sdk/runtime/tools/vectorstore.py +22 -21
  108. alita_sdk/runtime/tools/vectorstore_base.py +125 -52
  109. alita_sdk/runtime/utils/AlitaCallback.py +106 -20
  110. alita_sdk/runtime/utils/mcp_client.py +465 -0
  111. alita_sdk/runtime/utils/mcp_oauth.py +244 -0
  112. alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
  113. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  114. alita_sdk/runtime/utils/streamlit.py +40 -13
  115. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  116. alita_sdk/runtime/utils/utils.py +12 -0
  117. alita_sdk/tools/__init__.py +77 -33
  118. alita_sdk/tools/ado/repos/__init__.py +7 -6
  119. alita_sdk/tools/ado/repos/repos_wrapper.py +11 -11
  120. alita_sdk/tools/ado/test_plan/__init__.py +7 -7
  121. alita_sdk/tools/ado/wiki/__init__.py +7 -11
  122. alita_sdk/tools/ado/wiki/ado_wrapper.py +89 -15
  123. alita_sdk/tools/ado/work_item/__init__.py +7 -11
  124. alita_sdk/tools/ado/work_item/ado_wrapper.py +17 -8
  125. alita_sdk/tools/advanced_jira_mining/__init__.py +8 -7
  126. alita_sdk/tools/aws/delta_lake/__init__.py +11 -9
  127. alita_sdk/tools/azure_ai/search/__init__.py +7 -6
  128. alita_sdk/tools/base_indexer_toolkit.py +345 -70
  129. alita_sdk/tools/bitbucket/__init__.py +9 -8
  130. alita_sdk/tools/bitbucket/api_wrapper.py +50 -6
  131. alita_sdk/tools/browser/__init__.py +4 -4
  132. alita_sdk/tools/carrier/__init__.py +4 -6
  133. alita_sdk/tools/chunkers/__init__.py +3 -1
  134. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  135. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  136. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  137. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  138. alita_sdk/tools/cloud/aws/__init__.py +7 -6
  139. alita_sdk/tools/cloud/azure/__init__.py +7 -6
  140. alita_sdk/tools/cloud/gcp/__init__.py +7 -6
  141. alita_sdk/tools/cloud/k8s/__init__.py +7 -6
  142. alita_sdk/tools/code/linter/__init__.py +7 -7
  143. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  144. alita_sdk/tools/code/sonar/__init__.py +8 -7
  145. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  146. alita_sdk/tools/confluence/__init__.py +9 -8
  147. alita_sdk/tools/confluence/api_wrapper.py +171 -75
  148. alita_sdk/tools/confluence/loader.py +10 -0
  149. alita_sdk/tools/custom_open_api/__init__.py +9 -4
  150. alita_sdk/tools/elastic/__init__.py +8 -7
  151. alita_sdk/tools/elitea_base.py +492 -52
  152. alita_sdk/tools/figma/__init__.py +7 -7
  153. alita_sdk/tools/figma/api_wrapper.py +2 -1
  154. alita_sdk/tools/github/__init__.py +9 -9
  155. alita_sdk/tools/github/api_wrapper.py +9 -26
  156. alita_sdk/tools/github/github_client.py +62 -2
  157. alita_sdk/tools/gitlab/__init__.py +8 -8
  158. alita_sdk/tools/gitlab/api_wrapper.py +135 -33
  159. alita_sdk/tools/gitlab_org/__init__.py +7 -8
  160. alita_sdk/tools/google/bigquery/__init__.py +11 -12
  161. alita_sdk/tools/google_places/__init__.py +8 -7
  162. alita_sdk/tools/jira/__init__.py +9 -7
  163. alita_sdk/tools/jira/api_wrapper.py +100 -52
  164. alita_sdk/tools/keycloak/__init__.py +8 -7
  165. alita_sdk/tools/localgit/local_git.py +56 -54
  166. alita_sdk/tools/memory/__init__.py +1 -1
  167. alita_sdk/tools/non_code_indexer_toolkit.py +3 -2
  168. alita_sdk/tools/ocr/__init__.py +8 -7
  169. alita_sdk/tools/openapi/__init__.py +10 -1
  170. alita_sdk/tools/pandas/__init__.py +8 -7
  171. alita_sdk/tools/postman/__init__.py +7 -8
  172. alita_sdk/tools/postman/api_wrapper.py +19 -8
  173. alita_sdk/tools/postman/postman_analysis.py +8 -1
  174. alita_sdk/tools/pptx/__init__.py +8 -9
  175. alita_sdk/tools/qtest/__init__.py +16 -11
  176. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  177. alita_sdk/tools/rally/__init__.py +7 -8
  178. alita_sdk/tools/report_portal/__init__.py +9 -7
  179. alita_sdk/tools/salesforce/__init__.py +7 -7
  180. alita_sdk/tools/servicenow/__init__.py +10 -10
  181. alita_sdk/tools/sharepoint/__init__.py +7 -6
  182. alita_sdk/tools/sharepoint/api_wrapper.py +127 -36
  183. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  184. alita_sdk/tools/sharepoint/utils.py +8 -2
  185. alita_sdk/tools/slack/__init__.py +7 -6
  186. alita_sdk/tools/sql/__init__.py +8 -7
  187. alita_sdk/tools/sql/api_wrapper.py +71 -23
  188. alita_sdk/tools/testio/__init__.py +7 -6
  189. alita_sdk/tools/testrail/__init__.py +8 -9
  190. alita_sdk/tools/utils/__init__.py +26 -4
  191. alita_sdk/tools/utils/content_parser.py +88 -60
  192. alita_sdk/tools/utils/text_operations.py +254 -0
  193. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +76 -26
  194. alita_sdk/tools/xray/__init__.py +9 -7
  195. alita_sdk/tools/zephyr/__init__.py +7 -6
  196. alita_sdk/tools/zephyr_enterprise/__init__.py +8 -6
  197. alita_sdk/tools/zephyr_essential/__init__.py +7 -6
  198. alita_sdk/tools/zephyr_essential/api_wrapper.py +12 -13
  199. alita_sdk/tools/zephyr_scale/__init__.py +7 -6
  200. alita_sdk/tools/zephyr_squad/__init__.py +7 -6
  201. {alita_sdk-0.3.351.dist-info → alita_sdk-0.3.499.dist-info}/METADATA +147 -2
  202. {alita_sdk-0.3.351.dist-info → alita_sdk-0.3.499.dist-info}/RECORD +206 -130
  203. alita_sdk-0.3.499.dist-info/entry_points.txt +2 -0
  204. {alita_sdk-0.3.351.dist-info → alita_sdk-0.3.499.dist-info}/WHEEL +0 -0
  205. {alita_sdk-0.3.351.dist-info → alita_sdk-0.3.499.dist-info}/licenses/LICENSE +0 -0
  206. {alita_sdk-0.3.351.dist-info → alita_sdk-0.3.499.dist-info}/top_level.txt +0 -0
@@ -9,26 +9,19 @@ from ...elitea_base import filter_missconfigured_index_tools
9
9
  from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
12
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
13
13
 
14
14
  name = "azure_devops_wiki"
15
15
  name_alias = 'ado_wiki'
16
16
 
17
17
  class AzureDevOpsWikiToolkit(BaseToolkit):
18
18
  tools: List[BaseTool] = []
19
- toolkit_max_length: int = 0
20
19
 
21
20
  @staticmethod
22
21
  def toolkit_config_schema() -> BaseModel:
23
22
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
24
- AzureDevOpsWikiToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
25
23
  m = create_model(
26
24
  name_alias,
27
- name=(str, Field(description="Toolkit name",
28
- json_schema_extra={
29
- 'toolkit_name': True,
30
- 'max_toolkit_length': AzureDevOpsWikiToolkit.toolkit_max_length})
31
- ),
32
25
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
33
26
  # indexer settings
34
27
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None,
@@ -95,15 +88,18 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
95
88
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
96
89
  available_tools = azure_devops_api_wrapper.get_available_tools()
97
90
  tools = []
98
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
99
91
  for tool in available_tools:
100
92
  if selected_tools:
101
93
  if tool["name"] not in selected_tools:
102
94
  continue
95
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
96
+ if toolkit_name:
97
+ description = f"{description}\nToolkit: {toolkit_name}"
98
+ description = description[:1000]
103
99
  tools.append(BaseAction(
104
100
  api_wrapper=azure_devops_api_wrapper,
105
- name=prefix + tool["name"],
106
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
101
+ name=tool["name"],
102
+ description=description,
107
103
  args_schema=tool["args_schema"]
108
104
  ))
109
105
  return cls(tools=tools)
@@ -1,5 +1,7 @@
1
1
  import hashlib
2
2
  import logging
3
+ import re
4
+ import requests
3
5
  from typing import Generator, Literal, Optional
4
6
 
5
7
  from azure.devops.connection import Connection
@@ -15,8 +17,11 @@ from pydantic import create_model, PrivateAttr, SecretStr
15
17
  from pydantic import model_validator
16
18
  from pydantic.fields import Field
17
19
 
20
+ import alita_sdk.tools.ado.work_item
21
+ from ..repos import ReposApiWrapper
18
22
  from ...non_code_indexer_toolkit import NonCodeIndexerToolkit
19
23
  from ...utils.available_tools_decorator import extend_with_parent_available_tools
24
+ from ...utils.content_parser import parse_file_content
20
25
  from ....runtime.utils.utils import IndexerKeywords
21
26
 
22
27
  logger = logging.getLogger(__name__)
@@ -29,13 +34,17 @@ GetWikiInput = create_model(
29
34
  GetPageByPathInput = create_model(
30
35
  "GetPageByPathInput",
31
36
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
32
- 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))
33
40
  )
34
41
 
35
42
  GetPageByIdInput = create_model(
36
43
  "GetPageByIdInput",
37
44
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
38
- 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))
39
48
  )
40
49
 
41
50
  ModifyPageInput = create_model(
@@ -43,8 +52,9 @@ ModifyPageInput = create_model(
43
52
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
44
53
  page_name=(str, Field(description="Wiki page name")),
45
54
  page_content=(str, Field(description="Wiki page content")),
46
- version_identifier=(str, Field(description="Version string identifier (name of tag/branch, SHA1 of commit)")),
47
- 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))
48
58
  )
49
59
 
50
60
  RenamePageInput = create_model(
@@ -57,6 +67,19 @@ RenamePageInput = create_model(
57
67
  )
58
68
 
59
69
 
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
+
60
83
  class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
61
84
  # TODO use ado_configuration instead of organization_url, project and token
62
85
  organization_url: str
@@ -94,24 +117,75 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
94
117
  logger.error(f"Error during the attempt to extract wiki: {str(e)}")
95
118
  return ToolException(f"Error during the attempt to extract wiki: {str(e)}")
96
119
 
97
- 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):
98
121
  """Extract ADO wiki page content."""
99
122
  try:
100
- return self._client.get_page(project=self.project, wiki_identifier=wiki_identified, path=page_name,
101
- 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)
102
126
  except Exception as e:
103
127
  logger.error(f"Error during the attempt to extract wiki page: {str(e)}")
104
128
  return ToolException(f"Error during the attempt to extract wiki page: {str(e)}")
105
129
 
106
- 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):
107
131
  """Extract ADO wiki page content."""
108
132
  try:
109
- return (self._client.get_page_by_id(project=self.project, wiki_identifier=wiki_identified, id=page_id,
110
- 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)
111
136
  except Exception as e:
112
137
  logger.error(f"Error during the attempt to extract wiki page: {str(e)}")
113
138
  return ToolException(f"Error during the attempt to extract wiki page: {str(e)}")
114
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
+
115
189
  def delete_page_by_path(self, wiki_identified: str, page_name: str):
116
190
  """Extract ADO wiki page content."""
117
191
  try:
@@ -166,7 +240,7 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
166
240
  logger.error(f"Unable to rename wiki page: {str(e)}")
167
241
  return ToolException(f"Unable to rename wiki page: {str(e)}")
168
242
 
169
- 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):
170
244
  """Create or Update ADO wiki page content."""
171
245
  try:
172
246
  all_wikis = [wiki.name for wiki in self._client.get_all_wikis(project=self.project)]
@@ -197,24 +271,24 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
197
271
  return ToolException(f"Unable to extract page by path {page_name}: {str(get_page_e)}")
198
272
 
199
273
  try:
200
- return self._client.create_or_update_page(
274
+ return _format_wiki_page_response(self._client.create_or_update_page(
201
275
  project=self.project,
202
276
  wiki_identifier=wiki_identified,
203
277
  path=page_name,
204
278
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
205
279
  version=version,
206
280
  version_descriptor=GitVersionDescriptor(version=version_identifier, version_type=version_type)
207
- )
281
+ ), expanded=expanded)
208
282
  except AzureDevOpsServiceError as e:
209
283
  if "The version '{0}' either is invalid or does not exist." in str(e):
210
284
  # Retry the request without version_descriptor
211
- return self._client.create_or_update_page(
285
+ return _format_wiki_page_response(wiki_page_response=self._client.create_or_update_page(
212
286
  project=self.project,
213
287
  wiki_identifier=wiki_identified,
214
288
  path=page_name,
215
289
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
216
290
  version=version
217
- )
291
+ ), expanded=expanded)
218
292
  else:
219
293
  raise
220
294
  except Exception as e:
@@ -9,25 +9,18 @@ from ...elitea_base import filter_missconfigured_index_tools
9
9
  from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
12
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
13
13
 
14
14
  name = "ado_boards"
15
15
 
16
16
  class AzureDevOpsWorkItemsToolkit(BaseToolkit):
17
17
  tools: List[BaseTool] = []
18
- toolkit_max_length: int = 0
19
18
 
20
19
  @staticmethod
21
20
  def toolkit_config_schema() -> BaseModel:
22
21
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
23
- AzureDevOpsWorkItemsToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
24
22
  m = create_model(
25
23
  name,
26
- name=(str, Field(description="Toolkit name",
27
- json_schema_extra={
28
- 'toolkit_name': True,
29
- 'max_toolkit_length': AzureDevOpsWorkItemsToolkit.toolkit_max_length})
30
- ),
31
24
  ado_configuration=(AdoConfiguration, Field(description="Ado Work Item configuration", json_schema_extra={'configuration_types': ['ado']})),
32
25
  limit=(Optional[int], Field(description="ADO plans limit used for limitation of the list with results", default=5)),
33
26
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
@@ -96,15 +89,18 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
96
89
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
97
90
  available_tools = azure_devops_api_wrapper.get_available_tools()
98
91
  tools = []
99
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
100
92
  for tool in available_tools:
101
93
  if selected_tools:
102
94
  if tool["name"] not in selected_tools:
103
95
  continue
96
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
97
+ if toolkit_name:
98
+ description = f"{description}\nToolkit: {toolkit_name}"
99
+ description = description[:1000]
104
100
  tools.append(BaseAction(
105
101
  api_wrapper=azure_devops_api_wrapper,
106
- name=prefix + tool["name"],
107
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
102
+ name=tool["name"],
103
+ description=description,
108
104
  args_schema=tool["args_schema"]
109
105
  ))
110
106
  return cls(tools=tools)
@@ -329,11 +329,14 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
329
329
  parsed_item.update(fields_data)
330
330
 
331
331
  # extract relations if any
332
- 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
333
338
  if relations_data:
334
- parsed_item['relations'] = []
335
- for relation in relations_data:
336
- parsed_item['relations'].append(relation.as_dict())
339
+ parsed_item['relations'] = [relation.as_dict() for relation in relations_data]
337
340
 
338
341
  if parse_attachments:
339
342
  # describe images in work item fields if present
@@ -344,13 +347,19 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
344
347
  for img in images:
345
348
  src = img.get('src')
346
349
  if src:
347
- description = self.parse_attachment_by_url(src, image_description_prompt)
350
+ description = self.parse_attachment_by_url(src, image_description_prompt=image_description_prompt)
348
351
  img['image-description'] = description
349
352
  parsed_item[field_name] = str(soup)
350
353
  # parse attached documents if present
351
- if parsed_item['relations']:
352
- for attachment in parsed_item['relations']:
353
- attachment['content'] = self.parse_attachment_by_url(attachment['url'], attachment['attributes']['name'], image_description_prompt)
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}")
354
363
 
355
364
 
356
365
  return parsed_item
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, Field, SecretStr
6
6
  from .data_mining_wrapper import AdvancedJiraMiningWrapper
7
7
  from ..base.tool import BaseAction
8
8
  from ..elitea_base import filter_missconfigured_index_tools
9
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ..utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "advanced_jira_mining"
12
12
 
@@ -28,15 +28,13 @@ def get_tools(tool):
28
28
 
29
29
  class AdvancedJiraMiningToolkit(BaseToolkit):
30
30
  tools: List[BaseTool] = []
31
- toolkit_max_length: int = 0
32
31
 
33
32
  @staticmethod
34
33
  def toolkit_config_schema() -> BaseModel:
35
34
  selected_tools = {x['name']: x['args_schema'].schema() for x in AdvancedJiraMiningWrapper.model_construct().get_available_tools()}
36
- AdvancedJiraMiningToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
37
35
  return create_model(
38
36
  name,
39
- jira_base_url=(str, Field(default="", title="Jira URL", description="Jira URL", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': AdvancedJiraMiningToolkit.toolkit_max_length})),
37
+ jira_base_url=(str, Field(default="", title="Jira URL", description="Jira URL", json_schema_extra={'toolkit_name': True})),
40
38
  confluence_base_url=(str, Field(default="", title="Confluence URL", description="Confluence URL")),
41
39
  model_type=(str, Field(default="", title="Model type", description="Model type")),
42
40
  summarization_prompt=(Optional[str], Field(default=None, title="Summarization prompt", description="Summarization prompt")),
@@ -66,16 +64,19 @@ class AdvancedJiraMiningToolkit(BaseToolkit):
66
64
  selected_tools = []
67
65
  jira_mining_wrapper = AdvancedJiraMiningWrapper(**kwargs)
68
66
  available_tools = jira_mining_wrapper.get_available_tools()
69
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
70
67
  tools = []
71
68
  for tool in available_tools:
72
69
  if selected_tools:
73
70
  if tool["name"] not in selected_tools:
74
71
  continue
72
+ description = tool["description"]
73
+ if toolkit_name:
74
+ description = f"Toolkit: {toolkit_name}\n{description}"
75
+ description = description[:1000]
75
76
  tools.append(BaseAction(
76
77
  api_wrapper=jira_mining_wrapper,
77
- name=prefix + tool["name"],
78
- description=tool["description"],
78
+ name=tool["name"],
79
+ description=description,
79
80
  args_schema=tool["args_schema"]
80
81
  ))
81
82
  return cls(tools=tools)
@@ -6,7 +6,7 @@ from langchain_core.tools import BaseTool, BaseToolkit
6
6
  from pydantic import BaseModel, Field, computed_field, field_validator
7
7
 
8
8
  from alita_sdk.configurations.delta_lake import DeltaLakeConfiguration
9
- from ...utils import TOOLKIT_SPLITTER, clean_string, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
  from .api_wrapper import DeltaLakeApiWrapper
11
11
  from .tool import DeltaLakeAction
12
12
 
@@ -21,10 +21,6 @@ def get_available_tools() -> dict[str, dict]:
21
21
  }
22
22
  return available_tools
23
23
 
24
- toolkit_max_length = lru_cache(maxsize=1)(
25
- lambda: get_max_toolkit_length(get_available_tools())
26
- )
27
-
28
24
  class DeltaLakeToolkitConfig(BaseModel):
29
25
  class Config:
30
26
  title = name
@@ -87,9 +83,10 @@ class DeltaLakeToolkit(BaseToolkit):
87
83
 
88
84
  @computed_field
89
85
  @property
90
- def tool_prefix(self) -> str:
86
+ def toolkit_context(self) -> str:
87
+ """Returns toolkit context for descriptions (max 1000 chars)."""
91
88
  return (
92
- clean_string(self.toolkit_name, toolkit_max_length()) + TOOLKIT_SPLITTER
89
+ f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
93
90
  if self.toolkit_name
94
91
  else ""
95
92
  )
@@ -118,11 +115,16 @@ class DeltaLakeToolkit(BaseToolkit):
118
115
  selected_tools = set(selected_tools)
119
116
  for t in instance.available_tools:
120
117
  if t["name"] in selected_tools:
118
+ description = t["description"]
119
+ if toolkit_name:
120
+ description = f"Toolkit: {toolkit_name}\n{description}"
121
+ description = f"S3 Path: {getattr(instance.api_wrapper, 's3_path', '')} Table Path: {getattr(instance.api_wrapper, 'table_path', '')}\n{description}"
122
+ description = description[:1000]
121
123
  instance.tools.append(
122
124
  DeltaLakeAction(
123
125
  api_wrapper=instance.api_wrapper,
124
- name=instance.tool_prefix + t["name"],
125
- description=f"S3 Path: {getattr(instance.api_wrapper, 's3_path', '')} Table Path: {getattr(instance.api_wrapper, 'table_path', '')}\n" + t["description"],
126
+ name=t["name"],
127
+ description=description,
126
128
  args_schema=t["args_schema"],
127
129
  )
128
130
  )
@@ -6,7 +6,7 @@ from langchain_core.tools import BaseToolkit, BaseTool
6
6
  from pydantic import create_model, BaseModel, ConfigDict, Field
7
7
 
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
9
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
10
10
  from ....configurations.azure_search import AzureSearchConfiguration
11
11
  import requests
12
12
 
@@ -31,12 +31,10 @@ def get_toolkit():
31
31
 
32
32
  class AzureSearchToolkit(BaseToolkit):
33
33
  tools: List[BaseTool] = []
34
- toolkit_max_length: int = 0
35
34
 
36
35
  @staticmethod
37
36
  def toolkit_config_schema() -> BaseModel:
38
37
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureSearchApiWrapper.model_construct().get_available_tools()}
39
- AzureSearchToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
40
38
  m = create_model(
41
39
  name,
42
40
  index_name=(str, Field(description="Azure Search index name")),
@@ -79,16 +77,19 @@ class AzureSearchToolkit(BaseToolkit):
79
77
  }
80
78
  azure_search_api_wrapper = AzureSearchApiWrapper(**wrapper_payload)
81
79
  available_tools = azure_search_api_wrapper.get_available_tools()
82
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
83
80
  tools = []
84
81
  for tool in available_tools:
85
82
  if selected_tools:
86
83
  if tool["name"] not in selected_tools:
87
84
  continue
85
+ description = tool["description"]
86
+ if toolkit_name:
87
+ description = f"Toolkit: {toolkit_name}\n{description}"
88
+ description = description[:1000]
88
89
  tools.append(BaseAction(
89
90
  api_wrapper=azure_search_api_wrapper,
90
- name=prefix + tool["name"],
91
- description=tool["description"],
91
+ name=tool["name"],
92
+ description=description,
92
93
  args_schema=tool["args_schema"]
93
94
  ))
94
95
  return cls(tools=tools)