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,12 +1,15 @@
1
1
  from typing import List, Literal, Optional
2
2
 
3
3
  from langchain_core.tools import BaseToolkit, BaseTool
4
- from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
4
+ from pydantic import create_model, BaseModel, ConfigDict, Field
5
5
 
6
6
  from .api_wrapper import SQLApiWrapper
7
7
  from ..base.tool import BaseAction
8
8
  from .models import SQLDialect
9
- from ..utils import TOOLKIT_SPLITTER, clean_string, get_max_toolkit_length
9
+ from ..elitea_base import filter_missconfigured_index_tools
10
+ from ..utils import clean_string, get_max_toolkit_length
11
+ from ...configurations.sql import SqlConfiguration
12
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
10
13
 
11
14
  name = "sql"
12
15
 
@@ -14,32 +17,24 @@ def get_tools(tool):
14
17
  return SQLToolkit().get_toolkit(
15
18
  selected_tools=tool['settings'].get('selected_tools', []),
16
19
  dialect=tool['settings']['dialect'],
17
- host=tool['settings']['host'],
18
- port=tool['settings']['port'],
19
- username=tool['settings']['username'],
20
- password=tool['settings']['password'],
21
20
  database_name=tool['settings']['database_name'],
21
+ sql_configuration=tool['settings']['sql_configuration'],
22
22
  toolkit_name=tool.get('toolkit_name')
23
23
  ).get_tools()
24
24
 
25
25
 
26
26
  class SQLToolkit(BaseToolkit):
27
27
  tools: list[BaseTool] = []
28
- toolkit_max_length: int = 0
29
28
 
30
29
  @staticmethod
31
30
  def toolkit_config_schema() -> BaseModel:
32
31
  selected_tools = {x['name']: x['args_schema'].schema() for x in SQLApiWrapper.model_construct().get_available_tools()}
33
- SQLToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
34
32
  supported_dialects = (d.value for d in SQLDialect)
35
33
  return create_model(
36
34
  name,
37
35
  dialect=(Literal[tuple(supported_dialects)], Field(description="Database dialect (mysql or postgres)")),
38
- host=(str, Field(description="Database server address")),
39
- port=(str, Field(description="Database server port")),
40
- username=(str, Field(description="Database username")),
41
- password=(SecretStr, Field(description="Database password", json_schema_extra={'secret': True})),
42
- database_name=(str, Field(description="Database name", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': SQLToolkit.toolkit_max_length})),
36
+ database_name=(str, Field(description="Database name")),
37
+ sql_configuration=(SqlConfiguration, Field(description="SQL Configuration", json_schema_extra={'configuration_types': ['sql']})),
43
38
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
44
39
  __config__=ConfigDict(json_schema_extra=
45
40
  {
@@ -51,24 +46,32 @@ class SQLToolkit(BaseToolkit):
51
46
  )
52
47
 
53
48
  @classmethod
49
+ @filter_missconfigured_index_tools
54
50
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
55
51
  if selected_tools is None:
56
52
  selected_tools = []
57
- sql_api_wrapper = SQLApiWrapper(**kwargs)
58
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
53
+ wrapper_payload = {
54
+ **kwargs,
55
+ **kwargs.get('sql_configuration', {}),
56
+ }
57
+ sql_api_wrapper = SQLApiWrapper(**wrapper_payload)
59
58
  available_tools = sql_api_wrapper.get_available_tools()
60
59
  tools = []
61
60
  for tool in available_tools:
62
61
  if selected_tools and tool["name"] not in selected_tools:
63
62
  continue
63
+ description = f"{tool['description']}\nDatabase: {sql_api_wrapper.database_name}. Host: {sql_api_wrapper.host}"
64
+ if toolkit_name:
65
+ description = f"{description}\nToolkit: {toolkit_name}"
66
+ description = description[:1000]
64
67
  tools.append(BaseAction(
65
68
  api_wrapper=sql_api_wrapper,
66
- name=prefix + tool["name"],
67
- description=f"{tool['description']}\nDatabase: {sql_api_wrapper.database_name}. Host: {sql_api_wrapper.host}",
68
- args_schema=tool["args_schema"]
69
+ name=tool["name"],
70
+ description=description,
71
+ args_schema=tool["args_schema"],
72
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
69
73
  ))
70
74
  return cls(tools=tools)
71
75
 
72
76
  def get_tools(self) -> list[BaseTool]:
73
77
  return self.tools
74
-
@@ -1,8 +1,9 @@
1
1
  import logging
2
- from typing import Optional, Any
2
+ from typing import Optional
3
3
 
4
- from pydantic import BaseModel, create_model, model_validator, Field, SecretStr
5
- from pydantic.fields import PrivateAttr
4
+ from langchain_core.tools import ToolException
5
+ from pydantic import create_model, SecretStr, model_validator
6
+ from pydantic.fields import PrivateAttr, Field
6
7
  from sqlalchemy import create_engine, text, inspect, Engine
7
8
  from sqlalchemy.orm import sessionmaker
8
9
 
@@ -27,7 +28,7 @@ class SQLApiWrapper(BaseToolApiWrapper):
27
28
  username: str
28
29
  password: SecretStr
29
30
  database_name: str
30
- _client: Optional[Engine] = PrivateAttr()
31
+ _client: Optional[Engine] = PrivateAttr(default=None)
31
32
 
32
33
  @model_validator(mode='before')
33
34
  @classmethod
@@ -35,27 +36,73 @@ class SQLApiWrapper(BaseToolApiWrapper):
35
36
  for field in SQLConfig.model_fields:
36
37
  if field not in values or not values[field]:
37
38
  raise ValueError(f"{field} is a required field and must be provided.")
38
-
39
- dialect = values['dialect']
40
- host = values['host']
41
- username = values['username']
42
- password = values['password']
43
- database_name = values['database_name']
44
- port = values['port']
45
-
46
- if dialect == SQLDialect.POSTGRES:
47
- connection_string = f'postgresql+psycopg2://{username}:{password}@{host}:{port}/{database_name}'
48
- elif dialect == SQLDialect.MYSQL:
49
- connection_string = f'mysql+pymysql://{username}:{password}@{host}:{port}/{database_name}'
50
- else:
51
- raise ValueError(f"Unsupported database type. Supported types are: {[e.value for e in SQLDialect]}")
52
-
53
- cls._client = create_engine(connection_string)
54
39
  return values
55
40
 
41
+ def _mask_password_in_error(self, error_message: str) -> str:
42
+ """Mask password in error messages, showing only last 4 characters."""
43
+ password_str = self.password.get_secret_value()
44
+ if len(password_str) <= 4:
45
+ masked_password = "****"
46
+ else:
47
+ masked_password = "****" + password_str[-4:]
48
+
49
+ # Replace all occurrences of the password, and any substring of the password that may appear in the error message
50
+ for part in [password_str, password_str.replace('@', ''), password_str.split('@')[-1]]:
51
+ if part and part in error_message:
52
+ error_message = error_message.replace(part, masked_password)
53
+ return error_message
54
+
55
+ @property
56
+ def client(self) -> Engine:
57
+ """Lazy property to create and return database engine with error handling."""
58
+ if self._client is None:
59
+ try:
60
+ dialect = self.dialect
61
+ host = self.host
62
+ username = self.username
63
+ password = self.password.get_secret_value()
64
+ database_name = self.database_name
65
+ port = self.port
66
+
67
+ if dialect == SQLDialect.POSTGRES:
68
+ connection_string = f'postgresql+psycopg2://{username}:{password}@{host}:{port}/{database_name}'
69
+ elif dialect == SQLDialect.MYSQL:
70
+ connection_string = f'mysql+pymysql://{username}:{password}@{host}:{port}/{database_name}'
71
+ else:
72
+ raise ValueError(f"Unsupported database type. Supported types are: {[e.value for e in SQLDialect]}")
73
+
74
+ self._client = create_engine(connection_string)
75
+
76
+ # Test the connection
77
+ with self._client.connect() as conn:
78
+ conn.execute(text("SELECT 1"))
79
+
80
+ except Exception as e:
81
+ error_message = str(e)
82
+ masked_error = self._mask_password_in_error(error_message)
83
+ logger.error(f"Database connection failed: {masked_error}")
84
+ raise ValueError(f"Database connection failed: {masked_error}")
85
+
86
+ return self._client
87
+
88
+ def _handle_database_errors(func):
89
+ """Decorator to catch exceptions and mask passwords in error messages."""
90
+
91
+ def wrapper(self, *args, **kwargs):
92
+ try:
93
+ return func(self, *args, **kwargs)
94
+ except Exception as e:
95
+ error_message = str(e)
96
+ masked_error = self._mask_password_in_error(error_message)
97
+ logger.error(f"Database operation failed in {func.__name__}: {masked_error}")
98
+ raise ToolException(masked_error)
99
+
100
+ return wrapper
101
+
102
+ @_handle_database_errors
56
103
  def execute_sql(self, sql_query: str):
57
104
  """Executes the provided SQL query on the configured database."""
58
- engine = self._client
105
+ engine = self.client
59
106
  maker_session = sessionmaker(bind=engine)
60
107
  session = maker_session()
61
108
  try:
@@ -76,9 +123,10 @@ class SQLApiWrapper(BaseToolApiWrapper):
76
123
  finally:
77
124
  session.close()
78
125
 
126
+ @_handle_database_errors
79
127
  def list_tables_and_columns(self):
80
128
  """Lists all tables and their columns in the configured database."""
81
- inspector = inspect(self._client)
129
+ inspector = inspect(self.client)
82
130
  data = {}
83
131
  tables = inspector.get_table_names()
84
132
  for table in tables:
@@ -109,4 +157,4 @@ class SQLApiWrapper(BaseToolApiWrapper):
109
157
  "description": self.list_tables_and_columns.__doc__,
110
158
  "args_schema": SQLNoInput,
111
159
  }
112
- ]
160
+ ]
@@ -1,25 +1,25 @@
1
1
  from typing import List, Literal, Optional
2
2
 
3
3
  from langchain_core.tools import BaseToolkit, BaseTool
4
- from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
4
+ from pydantic import create_model, BaseModel, ConfigDict, Field
5
5
 
6
6
  from .api_wrapper import TestIOApiWrapper
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.testio import TestIOConfiguration
11
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
9
12
 
10
13
  name = "testio"
11
14
 
12
15
  def get_tools(tool):
13
16
  return TestIOToolkit().get_toolkit(
14
17
  selected_tools=tool['settings'].get('selected_tools', []),
15
- endpoint=tool['settings']['endpoint'],
16
- api_key=tool['settings']['api_key'],
18
+ testio_configuration=tool['settings']['testio_configuration'],
17
19
  toolkit_name=tool['toolkit_name']
18
20
  ).get_tools()
19
21
 
20
22
 
21
- TOOLKIT_MAX_LENGTH = 25
22
-
23
23
  class TestIOToolkit(BaseToolkit):
24
24
  tools: list[BaseTool] = []
25
25
 
@@ -29,8 +29,7 @@ class TestIOToolkit(BaseToolkit):
29
29
  selected_tools = {x['name']: x['args_schema'].schema() for x in TestIOApiWrapper.model_construct().get_available_tools()}
30
30
  return create_model(
31
31
  name,
32
- endpoint=(str, Field(description="TestIO endpoint", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': TOOLKIT_MAX_LENGTH})),
33
- api_key=(SecretStr, Field(description="API key", json_schema_extra={'secret': True})),
32
+ testio_configuration=(TestIOConfiguration, Field(description="TestIO Configuration", json_schema_extra={'configuration_types': ['testio']})),
34
33
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
35
34
  __config__=ConfigDict(json_schema_extra={'metadata': {"label": "TestIO", "icon_url": "testio-icon.svg",
36
35
  "categories": ["testing"],
@@ -38,21 +37,30 @@ class TestIOToolkit(BaseToolkit):
38
37
  )
39
38
 
40
39
  @classmethod
40
+ @filter_missconfigured_index_tools
41
41
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
42
42
  if selected_tools is None:
43
43
  selected_tools = []
44
- testio_api_wrapper = TestIOApiWrapper(**kwargs)
45
- prefix = clean_string(toolkit_name, TOOLKIT_MAX_LENGTH) + TOOLKIT_SPLITTER if toolkit_name else ''
44
+ wrapper_payload = {
45
+ **kwargs,
46
+ **kwargs.get('testio_configuration', {}),
47
+ }
48
+ testio_api_wrapper = TestIOApiWrapper(**wrapper_payload)
46
49
  available_tools = testio_api_wrapper.get_available_tools()
47
50
  tools = []
48
51
  for tool in available_tools:
49
52
  if selected_tools and tool["name"] not in selected_tools:
50
53
  continue
54
+ description = tool["description"]
55
+ if toolkit_name:
56
+ description = f"Toolkit: {toolkit_name}\n{description}"
57
+ description = description[:1000]
51
58
  tools.append(BaseAction(
52
59
  api_wrapper=testio_api_wrapper,
53
- name=prefix + tool["name"],
54
- description=tool["description"],
55
- args_schema=tool["args_schema"]
60
+ name=tool["name"],
61
+ description=description,
62
+ args_schema=tool["args_schema"],
63
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
56
64
  ))
57
65
  return cls(tools=tools)
58
66
 
@@ -6,16 +6,17 @@ import requests
6
6
 
7
7
  from .api_wrapper import TestrailAPIWrapper
8
8
  from ..base.tool import BaseAction
9
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
9
+ from ..elitea_base import filter_missconfigured_index_tools
10
+ from ..utils import clean_string, get_max_toolkit_length, check_connection_response
10
11
  from ...configurations.testrail import TestRailConfiguration
11
12
  from ...configurations.pgvector import PgVectorConfiguration
13
+ from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
12
14
 
13
15
  name = "testrail"
14
16
 
15
17
  def get_tools(tool):
16
18
  return TestrailToolkit().get_toolkit(
17
19
  selected_tools=tool['settings'].get('selected_tools', []),
18
- url=tool['settings']['url'],
19
20
  testrail_configuration=tool['settings']['testrail_configuration'],
20
21
  toolkit_name=tool.get('toolkit_name'),
21
22
  llm=tool['settings'].get('llm', None),
@@ -31,17 +32,12 @@ def get_tools(tool):
31
32
 
32
33
  class TestrailToolkit(BaseToolkit):
33
34
  tools: List[BaseTool] = []
34
- toolkit_max_length: int = 0
35
35
 
36
36
  @staticmethod
37
37
  def toolkit_config_schema() -> BaseModel:
38
38
  selected_tools = {x['name']: x['args_schema'].schema() for x in TestrailAPIWrapper.model_construct().get_available_tools()}
39
- TestrailToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
40
39
  m = create_model(
41
40
  name,
42
- name=(str, Field(description="Toolkit name", json_schema_extra={
43
- 'toolkit_name': True,
44
- "max_length": TestrailToolkit.toolkit_max_length})),
45
41
  testrail_configuration=(Optional[TestRailConfiguration], Field(description="TestRail Configuration", json_schema_extra={'configuration_types': ['testrail']})),
46
42
  pgvector_configuration=(Optional[PgVectorConfiguration], Field(default = None,
47
43
  description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
@@ -68,6 +64,7 @@ class TestrailToolkit(BaseToolkit):
68
64
  return m
69
65
 
70
66
  @classmethod
67
+ @filter_missconfigured_index_tools
71
68
  def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
72
69
  if selected_tools is None:
73
70
  selected_tools = []
@@ -78,18 +75,23 @@ class TestrailToolkit(BaseToolkit):
78
75
  **(kwargs.get('pgvector_configuration') or {}),
79
76
  }
80
77
  testrail_api_wrapper = TestrailAPIWrapper(**wrapper_payload)
81
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
82
78
  available_tools = testrail_api_wrapper.get_available_tools()
83
79
  tools = []
84
80
  for tool in available_tools:
85
81
  if selected_tools:
86
82
  if tool["name"] not in selected_tools:
87
83
  continue
84
+ description = tool["description"]
85
+ if toolkit_name:
86
+ description = f"Toolkit: {toolkit_name}\n{description}"
87
+ description = description + "\nTestrail instance: " + testrail_api_wrapper.url
88
+ description = description[:1000]
88
89
  tools.append(BaseAction(
89
90
  api_wrapper=testrail_api_wrapper,
90
- name=prefix + tool["name"],
91
- description=tool["description"] + "\nTestrail instance: " + testrail_api_wrapper.url,
92
- args_schema=tool["args_schema"]
91
+ name=tool["name"],
92
+ description=description,
93
+ args_schema=tool["args_schema"],
94
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
93
95
  ))
94
96
  return cls(tools=tools)
95
97