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
@@ -1,9 +1,17 @@
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 gitlab import GitlabGetError
6
+ from langchain_core.tools import ToolException
5
7
  from pydantic import create_model, Field, model_validator, SecretStr, PrivateAttr
6
8
 
9
+ from ..code_indexer_toolkit import CodeIndexerToolkit
10
+ from ..utils.available_tools_decorator import extend_with_parent_available_tools
11
+ from ..elitea_base import extend_with_file_operations, BaseCodeToolApiWrapper
12
+ from ..utils.content_parser import parse_file_content
13
+ from .utils import get_position
14
+
7
15
  AppendFileModel = create_model(
8
16
  "AppendFileModel",
9
17
  file_path=(str, Field(description="The path of the file")),
@@ -29,7 +37,7 @@ ReadFileModel = create_model(
29
37
  )
30
38
  UpdateFileModel = create_model(
31
39
  "UpdateFileModel",
32
- file_query=(str, Field(description="The file query string")),
40
+ file_query=(str, Field(description="The file query string containing file path on first line, followed by OLD/NEW markers. Format: file_path\\nOLD <<<< old content >>>> OLD\\nNEW <<<< new content >>>> NEW")),
33
41
  branch=(str, Field(description="The branch to update the file in")),
34
42
  )
35
43
  CommentOnIssueModel = create_model(
@@ -46,6 +54,11 @@ CreatePullRequestModel = create_model(
46
54
  pr_body=(str, Field(description="The body of the pull request")),
47
55
  branch=(str, Field(description="The branch to create the pull request from")),
48
56
  )
57
+ CommentOnPRModel = create_model(
58
+ "CommentOnPRModel",
59
+ pr_number=(int, Field(description="The number of the pull request/merge request")),
60
+ comment=(str, Field(description="The comment text to add")),
61
+ )
49
62
 
50
63
  CreateBranchModel = create_model(
51
64
  "CreateBranchModel",
@@ -84,9 +97,9 @@ GetPRChangesModel = create_model(
84
97
  CreatePRChangeCommentModel = create_model(
85
98
  "CreatePRChangeCommentModel",
86
99
  pr_number=(int, Field(description="GitLab Merge Request (Pull Request) number")),
87
- file_path=(str, Field(description="File path of the changed file")),
88
- line_number=(int, Field(description="Line number from the diff for a changed file")),
89
- comment=(str, Field(description="Comment content")),
100
+ file_path=(str, Field(description="File path of the changed file as shown in the diff")),
101
+ line_number=(int, Field(description="Line index (0-based) from the diff output. Use get_pr_changes first to see the diff and identify the correct line index to comment on.")),
102
+ comment=(str, Field(description="Comment content to add to the specific line")),
90
103
  )
91
104
  GetCommitsModel = create_model(
92
105
  "GetCommitsModel",
@@ -97,53 +110,67 @@ GetCommitsModel = create_model(
97
110
  author=(Optional[str], Field(description="Author name", default=None)),
98
111
  )
99
112
 
100
- class GitLabAPIWrapper(BaseCodeToolApiWrapper):
113
+ class GitLabAPIWrapper(CodeIndexerToolkit):
101
114
  url: str
102
115
  repository: str
103
116
  private_token: SecretStr
104
117
  branch: Optional[str] = 'main'
105
118
  _git: Any = PrivateAttr()
106
- _repo_instance: Any = PrivateAttr()
107
119
  _active_branch: Any = PrivateAttr()
108
-
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"
120
+
121
+ # Import file operation methods from BaseCodeToolApiWrapper
122
+ read_file_chunk = BaseCodeToolApiWrapper.read_file_chunk
123
+ read_multiple_files = BaseCodeToolApiWrapper.read_multiple_files
124
+ search_file = BaseCodeToolApiWrapper.search_file
125
+ edit_file = BaseCodeToolApiWrapper.edit_file
126
+
127
+ @staticmethod
128
+ def _sanitize_url(url: str) -> str:
129
+ """Remove trailing slash from URL if present."""
130
+ return url.rstrip('/') if url else url
120
131
 
121
132
  @model_validator(mode='before')
122
133
  @classmethod
123
- def validate_toolkit(cls, values: Dict) -> Dict:
134
+ def validate_toolkit_before(cls, values: Dict) -> Dict:
135
+ return super().validate_toolkit(values)
136
+
137
+ @model_validator(mode='after')
138
+ def validate_toolkit(self):
124
139
  try:
125
- import gitlab
140
+ import gitlab
126
141
  except ImportError:
127
142
  raise ImportError(
128
143
  "python-gitlab is not installed. "
129
144
  "Please install it with `pip install python-gitlab`"
130
145
  )
131
-
146
+ self.repository = self._sanitize_url(self.repository)
132
147
  g = gitlab.Gitlab(
133
- url=values['url'],
134
- private_token=values['private_token'],
148
+ url=self._sanitize_url(self.url),
149
+ private_token=self.private_token.get_secret_value(),
135
150
  keep_base_url=True,
136
151
  )
137
152
 
138
153
  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
154
+ self._git = g
155
+ self._active_branch = self.branch
156
+ return self
157
+
158
+ @property
159
+ def repo_instance(self):
160
+ if not hasattr(self, "_repo_instance") or self._repo_instance is None:
161
+ try:
162
+ if self._git and self.repository:
163
+ self._repo_instance = self._git.projects.get(self.repository)
164
+ else:
165
+ self._repo_instance = None
166
+ except Exception as e:
167
+ # Only raise when accessed, not during initialization
168
+ raise ToolException(e)
169
+ return self._repo_instance
143
170
 
144
171
  def set_active_branch(self, branch_name: str) -> str:
145
172
  self._active_branch = branch_name
146
- self._repo_instance.default_branch = branch_name
173
+ self.repo_instance.default_branch = branch_name
147
174
  return f"Active branch set to {branch_name}"
148
175
 
149
176
  def list_branches_in_repo(self, limit: Optional[int] = 20, branch_wildcard: Optional[str] = None) -> List[str]:
@@ -158,7 +185,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
158
185
  List[str]: List containing names of branches
159
186
  """
160
187
  try:
161
- branches = self._repo_instance.branches.list(get_all=True)
188
+ branches = self.repo_instance.branches.list(get_all=True)
162
189
 
163
190
  if branch_wildcard:
164
191
  branches = [branch for branch in branches if fnmatch.fnmatch(branch.name, branch_wildcard)]
@@ -185,7 +212,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
185
212
 
186
213
  def _get_all_files(self, path: str = None, recursive: bool = True, branch: str = None):
187
214
  branch = branch if branch else self._active_branch
188
- return self._repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
215
+ return self.repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
189
216
 
190
217
  # overrided for indexer
191
218
  def _get_files(self, path: str = None, recursive: bool = True, branch: str = None):
@@ -197,17 +224,31 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
197
224
  Get the commit hash of a file in a specific branch.
198
225
  """
199
226
  try:
200
- file = self._repo_instance.files.get(file_path, branch)
227
+ file = self.repo_instance.files.get(file_path, branch)
201
228
  return file.commit_id
202
229
  except Exception as e:
203
230
  return f"Unable to get commit hash for {file_path} due to error:\n{e}"
204
231
 
205
- def _read_file(self, file_path: str, branch: str):
232
+ def _read_file(self, file_path: str, branch: str, **kwargs):
233
+ """
234
+ Read a file from specified branch with optional partial read support.
235
+
236
+ Parameters:
237
+ file_path: the file path
238
+ branch: the branch to read the file from
239
+ **kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
240
+ partial read handled client-side by base class methods
241
+
242
+ Returns:
243
+ File content as string
244
+ """
245
+ # Default to active branch if branch is None, consistent with other methods
246
+ branch = branch if branch else self._active_branch
206
247
  return self.read_file(file_path, branch)
207
248
 
208
249
  def create_branch(self, branch_name: str) -> str:
209
250
  try:
210
- self._repo_instance.branches.create(
251
+ self.repo_instance.branches.create(
211
252
  {
212
253
  'branch': branch_name,
213
254
  'ref': self._active_branch,
@@ -230,7 +271,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
230
271
  return parsed
231
272
 
232
273
  def get_issues(self) -> str:
233
- issues = self._repo_instance.issues.list(state="opened")
274
+ issues = self.repo_instance.issues.list(state="opened")
234
275
  if len(issues) > 0:
235
276
  parsed_issues = self.parse_issues(issues)
236
277
  parsed_issues_str = (
@@ -241,7 +282,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
241
282
  return "No open issues available"
242
283
 
243
284
  def get_issue(self, issue_number: int) -> Dict[str, Any]:
244
- issue = self._repo_instance.issues.get(issue_number)
285
+ issue = self.repo_instance.issues.get(issue_number)
245
286
  page = 0
246
287
  comments: List[dict] = []
247
288
  while len(comments) <= 10:
@@ -267,7 +308,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
267
308
  commits are already in the {self.branch} branch"""
268
309
  else:
269
310
  try:
270
- pr = self._repo_instance.mergerequests.create(
311
+ pr = self.repo_instance.mergerequests.create(
271
312
  {
272
313
  "source_branch": branch,
273
314
  "target_branch": self.branch,
@@ -284,16 +325,39 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
284
325
  issue_number = int(comment_query.split("\n\n")[0])
285
326
  comment = comment_query[len(str(issue_number)) + 2 :]
286
327
  try:
287
- issue = self._repo_instance.issues.get(issue_number)
328
+ issue = self.repo_instance.issues.get(issue_number)
288
329
  issue.notes.create({"body": comment})
289
330
  return "Commented on issue " + str(issue_number)
290
331
  except Exception as e:
291
332
  return "Unable to make comment due to error:\n" + str(e)
292
333
 
334
+ def comment_on_pr(self, pr_number: int, comment: str) -> str:
335
+ """
336
+ Add a comment to a pull request (merge request) in GitLab.
337
+
338
+ This method adds a general comment to the entire merge request,
339
+ not tied to specific code lines or file changes.
340
+
341
+ Parameters:
342
+ pr_number: GitLab Merge Request (Pull Request) number
343
+ comment: Comment text to add
344
+
345
+ Returns:
346
+ Success message or error description
347
+ """
348
+ try:
349
+ mr = self.repo_instance.mergerequests.get(pr_number)
350
+ mr.notes.create({"body": comment})
351
+ return "Commented on merge request " + str(pr_number)
352
+ except Exception as e:
353
+ return "Unable to make comment due to error:\n" + str(e)
354
+
293
355
  def create_file(self, file_path: str, file_contents: str, branch: str) -> str:
356
+ # Default to active branch if branch is None
357
+ branch = branch if branch else self._active_branch
294
358
  try:
295
359
  self.set_active_branch(branch)
296
- self._repo_instance.files.get(file_path, branch)
360
+ self.repo_instance.files.get(file_path, branch)
297
361
  return f"File already exists at {file_path}. Use update_file instead"
298
362
  except Exception:
299
363
  data = {
@@ -302,53 +366,126 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
302
366
  "file_path": file_path,
303
367
  "content": file_contents,
304
368
  }
305
- self._repo_instance.files.create(data)
369
+ self.repo_instance.files.create(data)
306
370
 
307
371
  return "Created file " + file_path
308
372
 
309
373
  def read_file(self, file_path: str, branch: str) -> str:
374
+ # Default to active branch if branch is None
375
+ branch = branch if branch else self._active_branch
310
376
  self.set_active_branch(branch)
311
- file = self._repo_instance.files.get(file_path, branch)
312
- return file.decode().decode("utf-8")
377
+ file = self.repo_instance.files.get(file_path, branch)
378
+ return parse_file_content(file_name=file_path,
379
+ file_content=file.decode(),
380
+ llm=self.llm)
381
+
382
+ def _write_file(
383
+ self,
384
+ file_path: str,
385
+ content: str,
386
+ branch: str = None,
387
+ commit_message: str = None
388
+ ) -> str:
389
+ """
390
+ Write content to a file (create or update).
391
+
392
+ Parameters:
393
+ file_path: Path to the file
394
+ content: New file content
395
+ branch: Branch name (uses active branch if None)
396
+ commit_message: Commit message
397
+
398
+ Returns:
399
+ Success message
400
+ """
401
+ try:
402
+ branch = branch or self._active_branch
403
+
404
+ if branch == self.branch:
405
+ raise ToolException(
406
+ f"Cannot commit directly to the {self.branch} branch. "
407
+ "Please create a new branch and try again."
408
+ )
409
+
410
+ self.set_active_branch(branch)
411
+
412
+ # Check if file exists
413
+ try:
414
+ self.repo_instance.files.get(file_path, branch)
415
+ # File exists, update it
416
+ commit = {
417
+ "branch": branch,
418
+ "commit_message": commit_message or f"Update {file_path}",
419
+ "actions": [
420
+ {
421
+ "action": "update",
422
+ "file_path": file_path,
423
+ "content": content,
424
+ }
425
+ ],
426
+ }
427
+ self.repo_instance.commits.create(commit)
428
+ return f"Updated file {file_path}"
429
+ except:
430
+ # File doesn't exist, create it
431
+ data = {
432
+ "branch": branch,
433
+ "commit_message": commit_message or f"Create {file_path}",
434
+ "file_path": file_path,
435
+ "content": content,
436
+ }
437
+ self.repo_instance.files.create(data)
438
+ return f"Created file {file_path}"
439
+ except Exception as e:
440
+ raise ToolException(f"Unable to write file {file_path}: {str(e)}")
313
441
 
314
442
  def update_file(self, file_query: str, branch: str) -> str:
443
+ """
444
+ Update file using edit_file functionality.
445
+
446
+ This method now delegates to edit_file which uses OLD/NEW markers.
447
+ For backwards compatibility, it extracts the file_path from the query.
448
+
449
+ Expected format:
450
+ file_path
451
+ OLD <<<<
452
+ old content
453
+ >>>> OLD
454
+ NEW <<<<
455
+ new content
456
+ >>>> NEW
457
+
458
+ Args:
459
+ file_query: File path on first line, followed by OLD/NEW markers
460
+ branch: Branch to update the file in
461
+
462
+ Returns:
463
+ Success or error message
464
+ """
315
465
  if branch == self.branch:
316
466
  return (
317
- "You're attempting to commit to the directly"
467
+ "You're attempting to commit directly "
318
468
  f"to the {self.branch} branch, which is protected. "
319
469
  "Please create a new branch and try again."
320
470
  )
321
471
  try:
322
- file_path: str = file_query.split("\n")[0]
323
- self.set_active_branch(branch)
324
- file_content = self.read_file(file_path, branch)
325
- updated_file_content = file_content
326
- for old, new in self.extract_old_new_pairs(file_query):
327
- if not old.strip():
328
- continue
329
- updated_file_content = updated_file_content.replace(old, new)
330
-
331
- if file_content == updated_file_content:
472
+ # Extract file path from first line
473
+ lines = file_query.split("\n", 1)
474
+ if len(lines) < 2:
332
475
  return (
333
- "File content was not updated because old content was not found or empty."
334
- "It may be helpful to use the read_file action to get "
335
- "the current file contents."
476
+ "Invalid file_query format. Expected:\n"
477
+ "file_path\n"
478
+ "OLD <<<< old content >>>> OLD\n"
479
+ "NEW <<<< new content >>>> NEW"
336
480
  )
337
481
 
338
- commit = {
339
- "branch": branch,
340
- "commit_message": "Create " + file_path,
341
- "actions": [
342
- {
343
- "action": "update",
344
- "file_path": file_path,
345
- "content": updated_file_content,
346
- }
347
- ],
348
- }
482
+ file_path = lines[0].strip()
483
+ edit_content = lines[1] if len(lines) > 1 else ""
484
+
485
+ # Delegate to edit_file method with appropriate commit message
486
+ commit_message = f"Update {file_path}"
487
+ return self.edit_file(file_path, edit_content, branch, commit_message)
349
488
 
350
- self._repo_instance.commits.create(commit)
351
- return "Updated file " + file_path
352
489
  except Exception as e:
353
490
  return "Unable to update file due to error:\n" + str(e)
354
491
 
@@ -377,7 +514,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
377
514
  ],
378
515
  }
379
516
 
380
- self._repo_instance.commits.create(commit)
517
+ self.repo_instance.commits.create(commit)
381
518
  return "Updated file " + file_path
382
519
  except Exception as e:
383
520
  return "Unable to update file due to error:\n" + str(e)
@@ -387,23 +524,54 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
387
524
  self.set_active_branch(branch)
388
525
  if not commit_message:
389
526
  commit_message = f"Delete {file_path}"
390
- self._repo_instance.files.delete(file_path, branch, commit_message)
527
+ self.repo_instance.files.delete(file_path, branch, commit_message)
391
528
  return f"Deleted file {file_path}"
392
529
  except Exception as e:
393
530
  return f"Unable to delete file due to error:\n{e}"
394
531
 
395
532
  def get_pr_changes(self, pr_number: int) -> str:
396
- mr = self._repo_instance.mergerequests.get(pr_number)
533
+ mr = self.repo_instance.mergerequests.get(pr_number)
397
534
  res = f"title: {mr.title}\ndescription: {mr.description}\n\n"
398
535
  for change in mr.changes()["changes"]:
399
536
  res += f"diff --git a/{change['old_path']} b/{change['new_path']}\n{change['diff']}\n"
400
537
  return res
401
538
 
402
539
  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)
404
- position = {"position_type": "text", "new_path": file_path, "new_line": line_number}
405
- mr.discussions.create({"body": comment, "position": position})
406
- return "Comment added"
540
+ """
541
+ Create a comment on a specific line in a pull request (merge request) change in GitLab.
542
+
543
+ This method adds an inline comment to a specific line in the diff of a merge request.
544
+ The line_number parameter refers to the line index in the diff output (0-based),
545
+ not the line number in the original file.
546
+
547
+ **Important**: Use get_pr_changes first to see the diff and identify the correct
548
+ line index for commenting.
549
+
550
+ Parameters:
551
+ pr_number: GitLab Merge Request number
552
+ file_path: Path to the file being commented on (as shown in the diff)
553
+ line_number: Line index from the diff (0-based index)
554
+ comment: Comment text to add
555
+
556
+ Returns:
557
+ Success message or error description
558
+ """
559
+ try:
560
+ mr = self.repo_instance.mergerequests.get(pr_number)
561
+ except GitlabGetError as e:
562
+ if e.response_code == 404:
563
+ raise ToolException(f"Merge request number {pr_number} wasn't found: {e}")
564
+ raise ToolException(f"Error retrieving merge request {pr_number}: {e}")
565
+
566
+ try:
567
+ # Calculate proper position with SHA references and line mappings
568
+ position = get_position(file_path=file_path, line_number=line_number, mr=mr)
569
+
570
+ # Create discussion with the comment
571
+ mr.discussions.create({"body": comment, "position": position})
572
+ return f"Comment added successfully to line {line_number} in {file_path} on MR #{pr_number}"
573
+ except Exception as e:
574
+ raise ToolException(f"Failed to create comment on MR #{pr_number}: {e}")
407
575
 
408
576
  def get_commits(self, sha: Optional[str] = None, path: Optional[str] = None, since: Optional[str] = None, until: Optional[str] = None, author: Optional[str] = None):
409
577
  params = {}
@@ -417,7 +585,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
417
585
  params["until"] = until
418
586
  if author:
419
587
  params["author"] = author
420
- commits = self._repo_instance.commits.list(**params)
588
+ commits = self.repo_instance.commits.list(**params)
421
589
  return [
422
590
  {
423
591
  "sha": commit.id,
@@ -429,6 +597,8 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
429
597
  for commit in commits
430
598
  ]
431
599
 
600
+ @extend_with_parent_available_tools
601
+ @extend_with_file_operations
432
602
  def get_available_tools(self):
433
603
  return [
434
604
  {
@@ -479,6 +649,12 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
479
649
  "description": self.comment_on_issue.__doc__ or "Comment on an issue in the repository.",
480
650
  "args_schema": CommentOnIssueModel,
481
651
  },
652
+ {
653
+ "name": "comment_on_pr",
654
+ "ref": self.comment_on_pr,
655
+ "description": self.comment_on_pr.__doc__ or "Comment on a pull request (merge request) in the repository.",
656
+ "args_schema": CommentOnPRModel,
657
+ },
482
658
  {
483
659
  "name": "create_file",
484
660
  "ref": self.create_file,
@@ -524,7 +700,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
524
700
  {
525
701
  "name": "create_pr_change_comment",
526
702
  "ref": self.create_pr_change_comment,
527
- "description": "Create a comment on a pull request change.",
703
+ "description": self.create_pr_change_comment.__doc__ or "Create an inline comment on a specific line in a pull request change. Use get_pr_changes first to see the diff and identify the line index for commenting. The line_number is a 0-based index from the diff output, not the file line number.",
528
704
  "args_schema": CreatePRChangeCommentModel,
529
705
  },
530
706
  {
@@ -533,4 +709,4 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
533
709
  "description": "Retrieve a list of commits from the repository.",
534
710
  "args_schema": GetCommitsModel,
535
711
  }
536
- ] + self._get_vector_search_tools()
712
+ ]
@@ -4,8 +4,11 @@ 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
11
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
9
12
 
10
13
  name = "gitlab_org"
11
14
 
@@ -20,17 +23,13 @@ def get_tools(tool):
20
23
 
21
24
  class AlitaGitlabSpaceToolkit(BaseToolkit):
22
25
  tools: List[BaseTool] = []
23
- toolkit_max_length: int = 0
24
26
 
25
27
  @staticmethod
26
28
  def toolkit_config_schema() -> BaseModel:
27
29
  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
30
  return create_model(
30
31
  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",
32
+ gitlab_configuration=(GitlabConfiguration, Field(description="GitLab configuration",
34
33
  json_schema_extra={
35
34
  'configuration_types': ['gitlab']})),
36
35
  repositories=(str, Field(
@@ -51,6 +50,7 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
51
50
  )
52
51
 
53
52
  @classmethod
53
+ @filter_missconfigured_index_tools
54
54
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
55
55
  if selected_tools is None:
56
56
  selected_tools = []
@@ -60,18 +60,22 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
60
60
  **kwargs['gitlab_configuration'],
61
61
  }
62
62
  gitlab_wrapper = GitLabWorkspaceAPIWrapper(**wrapper_payload)
63
- prefix = clean_string(toolkit_name, AlitaGitlabSpaceToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
64
63
  available_tools = gitlab_wrapper.get_available_tools()
65
64
  tools = []
66
65
  for tool in available_tools:
67
66
  if selected_tools:
68
67
  if tool["name"] not in selected_tools:
69
68
  continue
69
+ description = tool["description"]
70
+ if toolkit_name:
71
+ description = f"Toolkit: {toolkit_name}\n{description}"
72
+ description = description[:1000]
70
73
  tools.append(BaseAction(
71
74
  api_wrapper=gitlab_wrapper,
72
- name=prefix + tool['name'],
73
- description=tool["description"],
74
- args_schema=tool["args_schema"]
75
+ name=tool['name'],
76
+ description=description,
77
+ args_schema=tool["args_schema"],
78
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
75
79
  ))
76
80
  return cls(tools=tools)
77
81
 
@@ -5,9 +5,10 @@ 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
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
11
12
 
12
13
  name = "bigquery"
13
14
 
@@ -22,11 +23,6 @@ def get_available_tools() -> dict[str, dict]:
22
23
  return available_tools
23
24
 
24
25
 
25
- toolkit_max_length = lru_cache(maxsize=1)(
26
- lambda: get_max_toolkit_length(get_available_tools())
27
- )
28
-
29
-
30
26
  class BigQueryToolkitConfig(BaseModel):
31
27
  class Config:
32
28
  title = name
@@ -46,7 +42,7 @@ class BigQueryToolkitConfig(BaseModel):
46
42
  }
47
43
  }
48
44
 
49
- bigquery_configuration: Optional[BigQueryConfiguration] = Field(
45
+ bigquery_configuration: BigQueryConfiguration = Field(
50
46
  description="BigQuery configuration", json_schema_extra={"configuration_types": ["bigquery"]}
51
47
  )
52
48
  selected_tools: List[str] = Field(
@@ -86,9 +82,10 @@ class BigQueryToolkit(BaseToolkit):
86
82
 
87
83
  @computed_field
88
84
  @property
89
- def tool_prefix(self) -> str:
85
+ def toolkit_context(self) -> str:
86
+ """Returns toolkit context for descriptions (max 1000 chars)."""
90
87
  return (
91
- clean_string(self.toolkit_name, toolkit_max_length()) + TOOLKIT_SPLITTER
88
+ f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
92
89
  if self.toolkit_name
93
90
  else ""
94
91
  )
@@ -122,14 +119,18 @@ class BigQueryToolkit(BaseToolkit):
122
119
  selected_tools = set(selected_tools)
123
120
  for t in instance.available_tools:
124
121
  if t["name"] in selected_tools:
122
+ description = t["description"]
123
+ if toolkit_name:
124
+ description = f"Toolkit: {toolkit_name}\n{description}"
125
+ description = f"Project: {getattr(instance.api_wrapper, 'project', '')}\n{description}"
126
+ description = description[:1000]
125
127
  instance.tools.append(
126
128
  BigQueryAction(
127
129
  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"],
130
+ name=t["name"],
131
+ description=description,
132
132
  args_schema=t["args_schema"],
133
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: t["name"]} if toolkit_name else {TOOL_NAME_META: t["name"]}
133
134
  )
134
135
  )
135
136
  return instance