alita-sdk 0.3.263__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 (248) 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/__init__.py +10 -0
  59. alita_sdk/configurations/ado.py +4 -2
  60. alita_sdk/configurations/azure_search.py +1 -1
  61. alita_sdk/configurations/bigquery.py +1 -1
  62. alita_sdk/configurations/bitbucket.py +94 -2
  63. alita_sdk/configurations/browser.py +18 -0
  64. alita_sdk/configurations/carrier.py +19 -0
  65. alita_sdk/configurations/confluence.py +96 -1
  66. alita_sdk/configurations/delta_lake.py +1 -1
  67. alita_sdk/configurations/figma.py +0 -5
  68. alita_sdk/configurations/github.py +65 -1
  69. alita_sdk/configurations/gitlab.py +79 -0
  70. alita_sdk/configurations/google_places.py +17 -0
  71. alita_sdk/configurations/jira.py +103 -0
  72. alita_sdk/configurations/postman.py +1 -1
  73. alita_sdk/configurations/qtest.py +1 -3
  74. alita_sdk/configurations/report_portal.py +19 -0
  75. alita_sdk/configurations/salesforce.py +19 -0
  76. alita_sdk/configurations/service_now.py +1 -12
  77. alita_sdk/configurations/sharepoint.py +19 -0
  78. alita_sdk/configurations/sonar.py +18 -0
  79. alita_sdk/configurations/sql.py +20 -0
  80. alita_sdk/configurations/testio.py +18 -0
  81. alita_sdk/configurations/testrail.py +88 -0
  82. alita_sdk/configurations/xray.py +94 -1
  83. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  84. alita_sdk/configurations/zephyr_essential.py +95 -0
  85. alita_sdk/runtime/clients/artifact.py +12 -2
  86. alita_sdk/runtime/clients/client.py +235 -66
  87. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  88. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  89. alita_sdk/runtime/clients/sandbox_client.py +373 -0
  90. alita_sdk/runtime/langchain/assistant.py +123 -17
  91. alita_sdk/runtime/langchain/constants.py +8 -1
  92. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  93. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  94. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +8 -2
  96. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  97. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  98. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  99. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  100. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  102. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  103. alita_sdk/runtime/langchain/document_loaders/constants.py +187 -40
  104. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  105. alita_sdk/runtime/langchain/langraph_agent.py +406 -91
  106. alita_sdk/runtime/langchain/utils.py +51 -8
  107. alita_sdk/runtime/llms/preloaded.py +2 -6
  108. alita_sdk/runtime/models/mcp_models.py +61 -0
  109. alita_sdk/runtime/toolkits/__init__.py +26 -0
  110. alita_sdk/runtime/toolkits/application.py +9 -2
  111. alita_sdk/runtime/toolkits/artifact.py +19 -7
  112. alita_sdk/runtime/toolkits/datasource.py +13 -6
  113. alita_sdk/runtime/toolkits/mcp.py +780 -0
  114. alita_sdk/runtime/toolkits/planning.py +178 -0
  115. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  116. alita_sdk/runtime/toolkits/tools.py +214 -60
  117. alita_sdk/runtime/toolkits/vectorstore.py +9 -4
  118. alita_sdk/runtime/tools/__init__.py +22 -0
  119. alita_sdk/runtime/tools/application.py +16 -4
  120. alita_sdk/runtime/tools/artifact.py +312 -19
  121. alita_sdk/runtime/tools/function.py +100 -4
  122. alita_sdk/runtime/tools/graph.py +81 -0
  123. alita_sdk/runtime/tools/image_generation.py +212 -0
  124. alita_sdk/runtime/tools/llm.py +539 -180
  125. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  126. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  127. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  128. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  129. alita_sdk/runtime/tools/planning/models.py +246 -0
  130. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  131. alita_sdk/runtime/tools/router.py +2 -1
  132. alita_sdk/runtime/tools/sandbox.py +375 -0
  133. alita_sdk/runtime/tools/vectorstore.py +62 -63
  134. alita_sdk/runtime/tools/vectorstore_base.py +156 -85
  135. alita_sdk/runtime/utils/AlitaCallback.py +106 -20
  136. alita_sdk/runtime/utils/mcp_client.py +465 -0
  137. alita_sdk/runtime/utils/mcp_oauth.py +244 -0
  138. alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
  139. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  140. alita_sdk/runtime/utils/streamlit.py +41 -14
  141. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  142. alita_sdk/runtime/utils/utils.py +14 -0
  143. alita_sdk/tools/__init__.py +78 -35
  144. alita_sdk/tools/ado/__init__.py +0 -1
  145. alita_sdk/tools/ado/repos/__init__.py +10 -6
  146. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -11
  147. alita_sdk/tools/ado/test_plan/__init__.py +10 -7
  148. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -23
  149. alita_sdk/tools/ado/wiki/__init__.py +10 -11
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -28
  151. alita_sdk/tools/ado/work_item/__init__.py +10 -11
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +63 -10
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +10 -7
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -11
  155. alita_sdk/tools/azure_ai/search/__init__.py +11 -7
  156. alita_sdk/tools/base_indexer_toolkit.py +392 -86
  157. alita_sdk/tools/bitbucket/__init__.py +18 -11
  158. alita_sdk/tools/bitbucket/api_wrapper.py +52 -9
  159. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  160. alita_sdk/tools/browser/__init__.py +40 -16
  161. alita_sdk/tools/browser/crawler.py +3 -1
  162. alita_sdk/tools/browser/utils.py +15 -6
  163. alita_sdk/tools/carrier/__init__.py +17 -17
  164. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  165. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  166. alita_sdk/tools/chunkers/__init__.py +3 -1
  167. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  168. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  169. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  170. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  171. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  172. alita_sdk/tools/cloud/aws/__init__.py +9 -6
  173. alita_sdk/tools/cloud/azure/__init__.py +9 -6
  174. alita_sdk/tools/cloud/gcp/__init__.py +9 -6
  175. alita_sdk/tools/cloud/k8s/__init__.py +9 -6
  176. alita_sdk/tools/code/linter/__init__.py +7 -7
  177. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  178. alita_sdk/tools/code/sonar/__init__.py +18 -12
  179. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  180. alita_sdk/tools/confluence/__init__.py +14 -11
  181. alita_sdk/tools/confluence/api_wrapper.py +198 -58
  182. alita_sdk/tools/confluence/loader.py +10 -0
  183. alita_sdk/tools/custom_open_api/__init__.py +9 -4
  184. alita_sdk/tools/elastic/__init__.py +8 -7
  185. alita_sdk/tools/elitea_base.py +543 -64
  186. alita_sdk/tools/figma/__init__.py +10 -8
  187. alita_sdk/tools/figma/api_wrapper.py +352 -153
  188. alita_sdk/tools/github/__init__.py +13 -11
  189. alita_sdk/tools/github/api_wrapper.py +9 -26
  190. alita_sdk/tools/github/github_client.py +75 -12
  191. alita_sdk/tools/github/schemas.py +2 -1
  192. alita_sdk/tools/gitlab/__init__.py +11 -10
  193. alita_sdk/tools/gitlab/api_wrapper.py +135 -45
  194. alita_sdk/tools/gitlab_org/__init__.py +11 -9
  195. alita_sdk/tools/google/bigquery/__init__.py +12 -13
  196. alita_sdk/tools/google_places/__init__.py +18 -10
  197. alita_sdk/tools/jira/__init__.py +14 -8
  198. alita_sdk/tools/jira/api_wrapper.py +315 -168
  199. alita_sdk/tools/keycloak/__init__.py +8 -7
  200. alita_sdk/tools/localgit/local_git.py +56 -54
  201. alita_sdk/tools/memory/__init__.py +27 -11
  202. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  203. alita_sdk/tools/ocr/__init__.py +8 -7
  204. alita_sdk/tools/openapi/__init__.py +10 -1
  205. alita_sdk/tools/pandas/__init__.py +8 -7
  206. alita_sdk/tools/pandas/api_wrapper.py +7 -25
  207. alita_sdk/tools/postman/__init__.py +8 -10
  208. alita_sdk/tools/postman/api_wrapper.py +19 -8
  209. alita_sdk/tools/postman/postman_analysis.py +8 -1
  210. alita_sdk/tools/pptx/__init__.py +8 -9
  211. alita_sdk/tools/qtest/__init__.py +19 -13
  212. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  213. alita_sdk/tools/rally/__init__.py +10 -9
  214. alita_sdk/tools/report_portal/__init__.py +20 -15
  215. alita_sdk/tools/salesforce/__init__.py +19 -15
  216. alita_sdk/tools/servicenow/__init__.py +14 -11
  217. alita_sdk/tools/sharepoint/__init__.py +14 -13
  218. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  219. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  220. alita_sdk/tools/sharepoint/utils.py +8 -2
  221. alita_sdk/tools/slack/__init__.py +10 -7
  222. alita_sdk/tools/sql/__init__.py +19 -18
  223. alita_sdk/tools/sql/api_wrapper.py +71 -23
  224. alita_sdk/tools/testio/__init__.py +18 -12
  225. alita_sdk/tools/testrail/__init__.py +10 -10
  226. alita_sdk/tools/testrail/api_wrapper.py +213 -45
  227. alita_sdk/tools/utils/__init__.py +28 -4
  228. alita_sdk/tools/utils/content_parser.py +181 -61
  229. alita_sdk/tools/utils/text_operations.py +254 -0
  230. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  231. alita_sdk/tools/xray/__init__.py +12 -7
  232. alita_sdk/tools/xray/api_wrapper.py +58 -113
  233. alita_sdk/tools/zephyr/__init__.py +9 -6
  234. alita_sdk/tools/zephyr_enterprise/__init__.py +13 -8
  235. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +17 -7
  236. alita_sdk/tools/zephyr_essential/__init__.py +13 -9
  237. alita_sdk/tools/zephyr_essential/api_wrapper.py +289 -47
  238. alita_sdk/tools/zephyr_essential/client.py +6 -4
  239. alita_sdk/tools/zephyr_scale/__init__.py +10 -7
  240. alita_sdk/tools/zephyr_scale/api_wrapper.py +6 -2
  241. alita_sdk/tools/zephyr_squad/__init__.py +9 -6
  242. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/METADATA +180 -33
  243. alita_sdk-0.3.499.dist-info/RECORD +433 -0
  244. alita_sdk-0.3.499.dist-info/entry_points.txt +2 -0
  245. alita_sdk-0.3.263.dist-info/RECORD +0 -342
  246. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/WHEEL +0 -0
  247. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/licenses/LICENSE +0 -0
  248. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,15 @@
1
1
  # api_wrapper.py
2
- from typing import Any, Dict, List, Optional
3
2
  import fnmatch
4
- from alita_sdk.tools.elitea_base import BaseCodeToolApiWrapper
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from langchain_core.tools import ToolException
5
6
  from pydantic import create_model, Field, model_validator, SecretStr, PrivateAttr
6
7
 
8
+ from ..code_indexer_toolkit import CodeIndexerToolkit
9
+ from ..utils.available_tools_decorator import extend_with_parent_available_tools
10
+ from ..elitea_base import extend_with_file_operations
11
+ from ..utils.content_parser import parse_file_content
12
+
7
13
  AppendFileModel = create_model(
8
14
  "AppendFileModel",
9
15
  file_path=(str, Field(description="The path of the file")),
@@ -97,53 +103,61 @@ GetCommitsModel = create_model(
97
103
  author=(Optional[str], Field(description="Author name", default=None)),
98
104
  )
99
105
 
100
- class GitLabAPIWrapper(BaseCodeToolApiWrapper):
106
+ class GitLabAPIWrapper(CodeIndexerToolkit):
101
107
  url: str
102
108
  repository: str
103
109
  private_token: SecretStr
104
110
  branch: Optional[str] = 'main'
105
111
  _git: Any = PrivateAttr()
106
- _repo_instance: Any = PrivateAttr()
107
112
  _active_branch: Any = PrivateAttr()
108
113
 
109
- llm: Optional[Any] = None
110
- # Alita instance
111
- alita: Optional[Any] = None
112
-
113
- # Vector store configuration
114
- connection_string: Optional[SecretStr] = None
115
- collection_name: Optional[str] = None
116
- doctype: Optional[str] = 'code'
117
- embedding_model: Optional[str] = "HuggingFaceEmbeddings"
118
- embedding_model_params: Optional[Dict[str, Any]] = {"model_name": "sentence-transformers/all-MiniLM-L6-v2"}
119
- vectorstore_type: Optional[str] = "PGVector"
114
+ @staticmethod
115
+ def _sanitize_url(url: str) -> str:
116
+ """Remove trailing slash from URL if present."""
117
+ return url.rstrip('/') if url else url
120
118
 
121
119
  @model_validator(mode='before')
122
120
  @classmethod
123
- def validate_toolkit(cls, values: Dict) -> Dict:
121
+ def validate_toolkit_before(cls, values: Dict) -> Dict:
122
+ return super().validate_toolkit(values)
123
+
124
+ @model_validator(mode='after')
125
+ def validate_toolkit(self):
124
126
  try:
125
- import gitlab
127
+ import gitlab
126
128
  except ImportError:
127
129
  raise ImportError(
128
130
  "python-gitlab is not installed. "
129
131
  "Please install it with `pip install python-gitlab`"
130
132
  )
131
-
133
+ self.repository = self._sanitize_url(self.repository)
132
134
  g = gitlab.Gitlab(
133
- url=values['url'],
134
- private_token=values['private_token'],
135
+ url=self._sanitize_url(self.url),
136
+ private_token=self.private_token.get_secret_value(),
135
137
  keep_base_url=True,
136
138
  )
137
139
 
138
140
  g.auth()
139
- cls._repo_instance = g.projects.get(values.get('repository'))
140
- cls._git = g
141
- cls._active_branch = values.get('branch')
142
- return values
141
+ self._git = g
142
+ self._active_branch = self.branch
143
+ return self
144
+
145
+ @property
146
+ def repo_instance(self):
147
+ if not hasattr(self, "_repo_instance") or self._repo_instance is None:
148
+ try:
149
+ if self._git and self.repository:
150
+ self._repo_instance = self._git.projects.get(self.repository)
151
+ else:
152
+ self._repo_instance = None
153
+ except Exception as e:
154
+ # Only raise when accessed, not during initialization
155
+ raise ToolException(e)
156
+ return self._repo_instance
143
157
 
144
158
  def set_active_branch(self, branch_name: str) -> str:
145
159
  self._active_branch = branch_name
146
- self._repo_instance.default_branch = branch_name
160
+ self.repo_instance.default_branch = branch_name
147
161
  return f"Active branch set to {branch_name}"
148
162
 
149
163
  def list_branches_in_repo(self, limit: Optional[int] = 20, branch_wildcard: Optional[str] = None) -> List[str]:
@@ -158,7 +172,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
158
172
  List[str]: List containing names of branches
159
173
  """
160
174
  try:
161
- branches = self._repo_instance.branches.list(get_all=True)
175
+ branches = self.repo_instance.branches.list(get_all=True)
162
176
 
163
177
  if branch_wildcard:
164
178
  branches = [branch for branch in branches if fnmatch.fnmatch(branch.name, branch_wildcard)]
@@ -185,7 +199,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
185
199
 
186
200
  def _get_all_files(self, path: str = None, recursive: bool = True, branch: str = None):
187
201
  branch = branch if branch else self._active_branch
188
- return self._repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
202
+ return self.repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
189
203
 
190
204
  # overrided for indexer
191
205
  def _get_files(self, path: str = None, recursive: bool = True, branch: str = None):
@@ -197,17 +211,29 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
197
211
  Get the commit hash of a file in a specific branch.
198
212
  """
199
213
  try:
200
- file = self._repo_instance.files.get(file_path, branch)
214
+ file = self.repo_instance.files.get(file_path, branch)
201
215
  return file.commit_id
202
216
  except Exception as e:
203
217
  return f"Unable to get commit hash for {file_path} due to error:\n{e}"
204
218
 
205
- def _read_file(self, file_path: str, branch: str):
219
+ def _read_file(self, file_path: str, branch: str, **kwargs):
220
+ """
221
+ Read a file from specified branch with optional partial read support.
222
+
223
+ Parameters:
224
+ file_path: the file path
225
+ branch: the branch to read the file from
226
+ **kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
227
+ partial read handled client-side by base class methods
228
+
229
+ Returns:
230
+ File content as string
231
+ """
206
232
  return self.read_file(file_path, branch)
207
233
 
208
234
  def create_branch(self, branch_name: str) -> str:
209
235
  try:
210
- self._repo_instance.branches.create(
236
+ self.repo_instance.branches.create(
211
237
  {
212
238
  'branch': branch_name,
213
239
  'ref': self._active_branch,
@@ -230,7 +256,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
230
256
  return parsed
231
257
 
232
258
  def get_issues(self) -> str:
233
- issues = self._repo_instance.issues.list(state="opened")
259
+ issues = self.repo_instance.issues.list(state="opened")
234
260
  if len(issues) > 0:
235
261
  parsed_issues = self.parse_issues(issues)
236
262
  parsed_issues_str = (
@@ -241,7 +267,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
241
267
  return "No open issues available"
242
268
 
243
269
  def get_issue(self, issue_number: int) -> Dict[str, Any]:
244
- issue = self._repo_instance.issues.get(issue_number)
270
+ issue = self.repo_instance.issues.get(issue_number)
245
271
  page = 0
246
272
  comments: List[dict] = []
247
273
  while len(comments) <= 10:
@@ -267,7 +293,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
267
293
  commits are already in the {self.branch} branch"""
268
294
  else:
269
295
  try:
270
- pr = self._repo_instance.mergerequests.create(
296
+ pr = self.repo_instance.mergerequests.create(
271
297
  {
272
298
  "source_branch": branch,
273
299
  "target_branch": self.branch,
@@ -284,7 +310,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
284
310
  issue_number = int(comment_query.split("\n\n")[0])
285
311
  comment = comment_query[len(str(issue_number)) + 2 :]
286
312
  try:
287
- issue = self._repo_instance.issues.get(issue_number)
313
+ issue = self.repo_instance.issues.get(issue_number)
288
314
  issue.notes.create({"body": comment})
289
315
  return "Commented on issue " + str(issue_number)
290
316
  except Exception as e:
@@ -293,7 +319,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
293
319
  def create_file(self, file_path: str, file_contents: str, branch: str) -> str:
294
320
  try:
295
321
  self.set_active_branch(branch)
296
- self._repo_instance.files.get(file_path, branch)
322
+ self.repo_instance.files.get(file_path, branch)
297
323
  return f"File already exists at {file_path}. Use update_file instead"
298
324
  except Exception:
299
325
  data = {
@@ -302,14 +328,76 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
302
328
  "file_path": file_path,
303
329
  "content": file_contents,
304
330
  }
305
- self._repo_instance.files.create(data)
331
+ self.repo_instance.files.create(data)
306
332
 
307
333
  return "Created file " + file_path
308
334
 
309
335
  def read_file(self, file_path: str, branch: str) -> str:
310
336
  self.set_active_branch(branch)
311
- file = self._repo_instance.files.get(file_path, branch)
312
- return file.decode().decode("utf-8")
337
+ file = self.repo_instance.files.get(file_path, branch)
338
+ return parse_file_content(file_name=file_path,
339
+ file_content=file.decode(),
340
+ llm=self.llm)
341
+
342
+ def _write_file(
343
+ self,
344
+ file_path: str,
345
+ content: str,
346
+ branch: str = None,
347
+ commit_message: str = None
348
+ ) -> str:
349
+ """
350
+ Write content to a file (create or update).
351
+
352
+ Parameters:
353
+ file_path: Path to the file
354
+ content: New file content
355
+ branch: Branch name (uses active branch if None)
356
+ commit_message: Commit message
357
+
358
+ Returns:
359
+ Success message
360
+ """
361
+ try:
362
+ branch = branch or self._active_branch
363
+
364
+ if branch == self.branch:
365
+ raise ToolException(
366
+ f"Cannot commit directly to the {self.branch} branch. "
367
+ "Please create a new branch and try again."
368
+ )
369
+
370
+ self.set_active_branch(branch)
371
+
372
+ # Check if file exists
373
+ try:
374
+ self.repo_instance.files.get(file_path, branch)
375
+ # File exists, update it
376
+ commit = {
377
+ "branch": branch,
378
+ "commit_message": commit_message or f"Update {file_path}",
379
+ "actions": [
380
+ {
381
+ "action": "update",
382
+ "file_path": file_path,
383
+ "content": content,
384
+ }
385
+ ],
386
+ }
387
+ self.repo_instance.commits.create(commit)
388
+ return f"Updated file {file_path}"
389
+ except:
390
+ # File doesn't exist, create it
391
+ data = {
392
+ "branch": branch,
393
+ "commit_message": commit_message or f"Create {file_path}",
394
+ "file_path": file_path,
395
+ "content": content,
396
+ }
397
+ self.repo_instance.files.create(data)
398
+ return f"Created file {file_path}"
399
+ except Exception as e:
400
+ raise ToolException(f"Unable to write file {file_path}: {str(e)}")
313
401
 
314
402
  def update_file(self, file_query: str, branch: str) -> str:
315
403
  if branch == self.branch:
@@ -347,7 +435,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
347
435
  ],
348
436
  }
349
437
 
350
- self._repo_instance.commits.create(commit)
438
+ self.repo_instance.commits.create(commit)
351
439
  return "Updated file " + file_path
352
440
  except Exception as e:
353
441
  return "Unable to update file due to error:\n" + str(e)
@@ -377,7 +465,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
377
465
  ],
378
466
  }
379
467
 
380
- self._repo_instance.commits.create(commit)
468
+ self.repo_instance.commits.create(commit)
381
469
  return "Updated file " + file_path
382
470
  except Exception as e:
383
471
  return "Unable to update file due to error:\n" + str(e)
@@ -387,20 +475,20 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
387
475
  self.set_active_branch(branch)
388
476
  if not commit_message:
389
477
  commit_message = f"Delete {file_path}"
390
- self._repo_instance.files.delete(file_path, branch, commit_message)
478
+ self.repo_instance.files.delete(file_path, branch, commit_message)
391
479
  return f"Deleted file {file_path}"
392
480
  except Exception as e:
393
481
  return f"Unable to delete file due to error:\n{e}"
394
482
 
395
483
  def get_pr_changes(self, pr_number: int) -> str:
396
- mr = self._repo_instance.mergerequests.get(pr_number)
484
+ mr = self.repo_instance.mergerequests.get(pr_number)
397
485
  res = f"title: {mr.title}\ndescription: {mr.description}\n\n"
398
486
  for change in mr.changes()["changes"]:
399
487
  res += f"diff --git a/{change['old_path']} b/{change['new_path']}\n{change['diff']}\n"
400
488
  return res
401
489
 
402
490
  def create_pr_change_comment(self, pr_number: int, file_path: str, line_number: int, comment: str) -> str:
403
- mr = self._repo_instance.mergerequests.get(pr_number)
491
+ mr = self.repo_instance.mergerequests.get(pr_number)
404
492
  position = {"position_type": "text", "new_path": file_path, "new_line": line_number}
405
493
  mr.discussions.create({"body": comment, "position": position})
406
494
  return "Comment added"
@@ -417,7 +505,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
417
505
  params["until"] = until
418
506
  if author:
419
507
  params["author"] = author
420
- commits = self._repo_instance.commits.list(**params)
508
+ commits = self.repo_instance.commits.list(**params)
421
509
  return [
422
510
  {
423
511
  "sha": commit.id,
@@ -429,6 +517,8 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
429
517
  for commit in commits
430
518
  ]
431
519
 
520
+ @extend_with_parent_available_tools
521
+ @extend_with_file_operations
432
522
  def get_available_tools(self):
433
523
  return [
434
524
  {
@@ -533,4 +623,4 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
533
623
  "description": "Retrieve a list of commits from the repository.",
534
624
  "args_schema": GetCommitsModel,
535
625
  }
536
- ] + self._get_vector_search_tools()
626
+ ]
@@ -4,7 +4,9 @@ from langchain_core.tools import BaseToolkit
4
4
  from langchain_core.tools import BaseTool
5
5
  from ..base.tool import BaseAction
6
6
  from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
7
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
7
+
8
+ from ..elitea_base import filter_missconfigured_index_tools
9
+ from ..utils import clean_string, get_max_toolkit_length
8
10
  from ...configurations.gitlab import GitlabConfiguration
9
11
 
10
12
  name = "gitlab_org"
@@ -20,17 +22,13 @@ def get_tools(tool):
20
22
 
21
23
  class AlitaGitlabSpaceToolkit(BaseToolkit):
22
24
  tools: List[BaseTool] = []
23
- toolkit_max_length: int = 0
24
25
 
25
26
  @staticmethod
26
27
  def toolkit_config_schema() -> BaseModel:
27
28
  selected_tools = {x['name']: x['args_schema'].schema() for x in GitLabWorkspaceAPIWrapper.model_construct().get_available_tools()}
28
- AlitaGitlabSpaceToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
29
29
  return create_model(
30
30
  name,
31
- name=(str, Field(description="Toolkit name", json_schema_extra={'toolkit_name': True,
32
- 'max_toolkit_length': AlitaGitlabSpaceToolkit.toolkit_max_length})),
33
- gitlab_configuration=(Optional[GitlabConfiguration], Field(description="GitLab configuration",
31
+ gitlab_configuration=(GitlabConfiguration, Field(description="GitLab configuration",
34
32
  json_schema_extra={
35
33
  'configuration_types': ['gitlab']})),
36
34
  repositories=(str, Field(
@@ -51,6 +49,7 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
51
49
  )
52
50
 
53
51
  @classmethod
52
+ @filter_missconfigured_index_tools
54
53
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
55
54
  if selected_tools is None:
56
55
  selected_tools = []
@@ -60,17 +59,20 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
60
59
  **kwargs['gitlab_configuration'],
61
60
  }
62
61
  gitlab_wrapper = GitLabWorkspaceAPIWrapper(**wrapper_payload)
63
- prefix = clean_string(toolkit_name, AlitaGitlabSpaceToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
64
62
  available_tools = gitlab_wrapper.get_available_tools()
65
63
  tools = []
66
64
  for tool in available_tools:
67
65
  if selected_tools:
68
66
  if tool["name"] not in selected_tools:
69
67
  continue
68
+ description = tool["description"]
69
+ if toolkit_name:
70
+ description = f"Toolkit: {toolkit_name}\n{description}"
71
+ description = description[:1000]
70
72
  tools.append(BaseAction(
71
73
  api_wrapper=gitlab_wrapper,
72
- name=prefix + tool['name'],
73
- description=tool["description"],
74
+ name=tool['name'],
75
+ description=description,
74
76
  args_schema=tool["args_schema"]
75
77
  ))
76
78
  return cls(tools=tools)
@@ -5,7 +5,7 @@ from langchain_core.tools import BaseTool, BaseToolkit
5
5
  from pydantic import BaseModel, Field, computed_field, field_validator
6
6
 
7
7
  from ....configurations.bigquery import BigQueryConfiguration
8
- from ...utils import TOOLKIT_SPLITTER, clean_string, get_max_toolkit_length
8
+ from ...utils import clean_string, get_max_toolkit_length
9
9
  from .api_wrapper import BigQueryApiWrapper
10
10
  from .tool import BigQueryAction
11
11
 
@@ -22,11 +22,6 @@ def get_available_tools() -> dict[str, dict]:
22
22
  return available_tools
23
23
 
24
24
 
25
- toolkit_max_length = lru_cache(maxsize=1)(
26
- lambda: get_max_toolkit_length(get_available_tools())
27
- )
28
-
29
-
30
25
  class BigQueryToolkitConfig(BaseModel):
31
26
  class Config:
32
27
  title = name
@@ -46,7 +41,7 @@ class BigQueryToolkitConfig(BaseModel):
46
41
  }
47
42
  }
48
43
 
49
- bigquery_configuration: Optional[BigQueryConfiguration] = Field(
44
+ bigquery_configuration: BigQueryConfiguration = Field(
50
45
  description="BigQuery configuration", json_schema_extra={"configuration_types": ["bigquery"]}
51
46
  )
52
47
  selected_tools: List[str] = Field(
@@ -86,9 +81,10 @@ class BigQueryToolkit(BaseToolkit):
86
81
 
87
82
  @computed_field
88
83
  @property
89
- def tool_prefix(self) -> str:
84
+ def toolkit_context(self) -> str:
85
+ """Returns toolkit context for descriptions (max 1000 chars)."""
90
86
  return (
91
- clean_string(self.toolkit_name, toolkit_max_length()) + TOOLKIT_SPLITTER
87
+ f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
92
88
  if self.toolkit_name
93
89
  else ""
94
90
  )
@@ -122,13 +118,16 @@ class BigQueryToolkit(BaseToolkit):
122
118
  selected_tools = set(selected_tools)
123
119
  for t in instance.available_tools:
124
120
  if t["name"] in selected_tools:
121
+ description = t["description"]
122
+ if toolkit_name:
123
+ description = f"Toolkit: {toolkit_name}\n{description}"
124
+ description = f"Project: {getattr(instance.api_wrapper, 'project', '')}\n{description}"
125
+ description = description[:1000]
125
126
  instance.tools.append(
126
127
  BigQueryAction(
127
128
  api_wrapper=instance.api_wrapper,
128
- name=instance.tool_prefix + t["name"],
129
- # set unique description for declared tools to differentiate the same methods for different toolkits
130
- description=f"Project: {getattr(instance.api_wrapper, 'project', '')}\n"
131
- + t["description"],
129
+ name=t["name"],
130
+ description=description,
132
131
  args_schema=t["args_schema"],
133
132
  )
134
133
  )
@@ -1,35 +1,35 @@
1
1
  from typing import List, Literal, Optional
2
2
  from langchain_core.tools import BaseToolkit, BaseTool
3
- from pydantic import create_model, BaseModel, ConfigDict, SecretStr
3
+ from pydantic import create_model, BaseModel, ConfigDict
4
4
  from pydantic.fields import Field
5
5
 
6
6
  from .api_wrapper import GooglePlacesAPIWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..elitea_base import filter_missconfigured_index_tools
9
+ from ..utils import clean_string, get_max_toolkit_length
10
+ from ...configurations.google_places import GooglePlacesConfiguration
9
11
 
10
12
  name = "google_places"
11
13
 
12
14
  def get_tools(tool):
13
15
  return GooglePlacesToolkit().get_toolkit(
14
16
  selected_tools=tool['settings'].get('selected_tools', []),
15
- api_key=tool['settings']['api_key'],
16
17
  results_count=tool['settings'].get('results_count'),
18
+ google_places_configuration=tool['settings']['google_places_configuration'],
17
19
  toolkit_name=tool.get('toolkit_name')
18
20
  ).get_tools()
19
21
 
20
22
 
21
23
  class GooglePlacesToolkit(BaseToolkit):
22
24
  tools: list[BaseTool] = []
23
- toolkit_max_length: int = 0
24
25
 
25
26
  @staticmethod
26
27
  def toolkit_config_schema() -> BaseModel:
27
28
  selected_tools = {x['name']: x['args_schema'].schema() for x in GooglePlacesAPIWrapper.model_construct().get_available_tools()}
28
- GooglePlacesToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
29
29
  return create_model(
30
30
  name,
31
- api_key=(SecretStr, Field(description="Google Places API key", json_schema_extra={'secret': True, 'max_toolkit_length': GooglePlacesToolkit.toolkit_max_length})),
32
31
  results_count=(Optional[int], Field(description="Results number to show", default=None)),
32
+ google_places_configuration=(GooglePlacesConfiguration, Field(description="Google Places Configuration", json_schema_extra={'configuration_types': ['google_places']})),
33
33
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
34
34
  __config__=ConfigDict(json_schema_extra=
35
35
  {
@@ -44,20 +44,28 @@ class GooglePlacesToolkit(BaseToolkit):
44
44
  )
45
45
 
46
46
  @classmethod
47
+ @filter_missconfigured_index_tools
47
48
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
48
49
  if selected_tools is None:
49
50
  selected_tools = []
50
- google_places_api_wrapper = GooglePlacesAPIWrapper(**kwargs)
51
- prefix = clean_string(toolkit_name, GooglePlacesToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
51
+ wrapper_payload = {
52
+ **kwargs,
53
+ **kwargs.get('google_places_configuration', {}),
54
+ }
55
+ google_places_api_wrapper = GooglePlacesAPIWrapper(**wrapper_payload)
52
56
  available_tools = google_places_api_wrapper.get_available_tools()
53
57
  tools = []
54
58
  for tool in available_tools:
55
59
  if selected_tools and tool["name"] not in selected_tools:
56
60
  continue
61
+ description = tool["description"]
62
+ if toolkit_name:
63
+ description = f"Toolkit: {toolkit_name}\n{description}"
64
+ description = description[:1000]
57
65
  tools.append(BaseAction(
58
66
  api_wrapper=google_places_api_wrapper,
59
- name=prefix + tool["name"],
60
- description=tool["description"],
67
+ name=tool["name"],
68
+ description=description,
61
69
  args_schema=tool["args_schema"]
62
70
  ))
63
71
  return cls(tools=tools)
@@ -5,7 +5,8 @@ from ..base.tool import BaseAction
5
5
  from pydantic import create_model, BaseModel, ConfigDict, Field
6
6
  import requests
7
7
 
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, parse_list, check_connection_response
8
+ from ..elitea_base import filter_missconfigured_index_tools
9
+ from ..utils import clean_string, get_max_toolkit_length, parse_list, check_connection_response
9
10
  from ...configurations.jira import JiraConfiguration
10
11
  from ...configurations.pgvector import PgVectorConfiguration
11
12
 
@@ -16,6 +17,7 @@ def get_tools(tool):
16
17
  selected_tools=tool['settings'].get('selected_tools', []),
17
18
  base_url=tool['settings'].get('base_url'),
18
19
  cloud=tool['settings'].get('cloud', True),
20
+ api_version=tool['settings'].get('api_version', '2'),
19
21
  jira_configuration=tool['settings']['jira_configuration'],
20
22
  limit=tool['settings'].get('limit', 5),
21
23
  labels=parse_list(tool['settings'].get('labels', [])),
@@ -35,12 +37,10 @@ def get_tools(tool):
35
37
 
36
38
  class JiraToolkit(BaseToolkit):
37
39
  tools: List[BaseTool] = []
38
- toolkit_max_length: int = 0
39
40
 
40
41
  @staticmethod
41
42
  def toolkit_config_schema() -> BaseModel:
42
43
  selected_tools = {x['name']: x['args_schema'].schema() for x in JiraApiWrapper.model_construct().get_available_tools()}
43
- JiraToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
44
44
 
45
45
  @check_connection_response
46
46
  def check_connection(self):
@@ -66,16 +66,17 @@ class JiraToolkit(BaseToolkit):
66
66
  name,
67
67
  cloud=(bool, Field(description="Hosting Option", json_schema_extra={'configuration': True})),
68
68
  limit=(int, Field(description="Limit issues. Default is 5", gt=0, default=5)),
69
+ api_version=(Literal['2', '3'], Field(description="Rest API version: optional. Default is 2", default="3")),
69
70
  labels=(Optional[str], Field(
70
71
  description="List of comma separated labels used for labeling of agent's created or updated entities",
71
72
  default=None,
72
73
  examples="alita,elitea;another-label"
73
74
  )),
74
75
  # optional field for custom headers as dictionary
75
- custom_headers=(Optional[dict], Field(description="Custom headers for API requests", default=None)),
76
+ custom_headers=(Optional[dict], Field(description="Custom headers for API requests", default={})),
76
77
  verify_ssl=(bool, Field(description="Verify SSL", default=True)),
77
78
  additional_fields=(Optional[str], Field(description="Additional fields", default="")),
78
- jira_configuration=(Optional[JiraConfiguration], Field(description="Jira Configuration", json_schema_extra={'configuration_types': ['jira']})),
79
+ jira_configuration=(JiraConfiguration, Field(description="Jira Configuration", json_schema_extra={'configuration_types': ['jira']})),
79
80
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default=None,
80
81
  description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
81
82
  # embedder settings
@@ -95,6 +96,7 @@ class JiraToolkit(BaseToolkit):
95
96
  return model
96
97
 
97
98
  @classmethod
99
+ @filter_missconfigured_index_tools
98
100
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
99
101
  if selected_tools is None:
100
102
  selected_tools = []
@@ -105,17 +107,21 @@ class JiraToolkit(BaseToolkit):
105
107
  **(kwargs.get('pgvector_configuration') or {}),
106
108
  }
107
109
  jira_api_wrapper = JiraApiWrapper(**wrapper_payload)
108
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
109
110
  available_tools = jira_api_wrapper.get_available_tools()
110
111
  tools = []
111
112
  for tool in available_tools:
112
113
  if selected_tools:
113
114
  if tool["name"] not in selected_tools:
114
115
  continue
116
+ description = tool["description"]
117
+ if toolkit_name:
118
+ description = f"Toolkit: {toolkit_name}\n{description}"
119
+ description = f"Jira instance: {jira_api_wrapper.url}\n{description}"
120
+ description = description[:1000]
115
121
  tools.append(BaseAction(
116
122
  api_wrapper=jira_api_wrapper,
117
- name=prefix + tool["name"],
118
- description=f"Tool for Jira: '{jira_api_wrapper.base_url}'\n{tool['description']}",
123
+ name=tool["name"],
124
+ description=description,
119
125
  args_schema=tool["args_schema"]
120
126
  ))
121
127
  return cls(tools=tools)