alita-sdk 0.3.379__py3-none-any.whl → 0.3.627__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 (278) 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 +156 -0
  6. alita_sdk/cli/agent_loader.py +245 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3113 -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/testcases/__init__.py +94 -0
  23. alita_sdk/cli/testcases/data_generation.py +119 -0
  24. alita_sdk/cli/testcases/discovery.py +96 -0
  25. alita_sdk/cli/testcases/executor.py +84 -0
  26. alita_sdk/cli/testcases/logger.py +85 -0
  27. alita_sdk/cli/testcases/parser.py +172 -0
  28. alita_sdk/cli/testcases/prompts.py +91 -0
  29. alita_sdk/cli/testcases/reporting.py +125 -0
  30. alita_sdk/cli/testcases/setup.py +108 -0
  31. alita_sdk/cli/testcases/test_runner.py +282 -0
  32. alita_sdk/cli/testcases/utils.py +39 -0
  33. alita_sdk/cli/testcases/validation.py +90 -0
  34. alita_sdk/cli/testcases/workflow.py +196 -0
  35. alita_sdk/cli/toolkit.py +327 -0
  36. alita_sdk/cli/toolkit_loader.py +85 -0
  37. alita_sdk/cli/tools/__init__.py +43 -0
  38. alita_sdk/cli/tools/approval.py +224 -0
  39. alita_sdk/cli/tools/filesystem.py +1751 -0
  40. alita_sdk/cli/tools/planning.py +389 -0
  41. alita_sdk/cli/tools/terminal.py +414 -0
  42. alita_sdk/community/__init__.py +72 -12
  43. alita_sdk/community/inventory/__init__.py +236 -0
  44. alita_sdk/community/inventory/config.py +257 -0
  45. alita_sdk/community/inventory/enrichment.py +2137 -0
  46. alita_sdk/community/inventory/extractors.py +1469 -0
  47. alita_sdk/community/inventory/ingestion.py +3172 -0
  48. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  49. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  50. alita_sdk/community/inventory/parsers/base.py +295 -0
  51. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  52. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  53. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  54. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  55. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  56. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  57. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  58. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  59. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  60. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  61. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  62. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  63. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  64. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  65. alita_sdk/community/inventory/patterns/loader.py +348 -0
  66. alita_sdk/community/inventory/patterns/registry.py +198 -0
  67. alita_sdk/community/inventory/presets.py +535 -0
  68. alita_sdk/community/inventory/retrieval.py +1403 -0
  69. alita_sdk/community/inventory/toolkit.py +173 -0
  70. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  71. alita_sdk/community/inventory/visualize.py +1370 -0
  72. alita_sdk/configurations/__init__.py +1 -1
  73. alita_sdk/configurations/ado.py +141 -20
  74. alita_sdk/configurations/bitbucket.py +94 -2
  75. alita_sdk/configurations/confluence.py +130 -1
  76. alita_sdk/configurations/figma.py +76 -0
  77. alita_sdk/configurations/gitlab.py +91 -0
  78. alita_sdk/configurations/jira.py +103 -0
  79. alita_sdk/configurations/openapi.py +329 -0
  80. alita_sdk/configurations/qtest.py +72 -1
  81. alita_sdk/configurations/report_portal.py +96 -0
  82. alita_sdk/configurations/sharepoint.py +148 -0
  83. alita_sdk/configurations/testio.py +83 -0
  84. alita_sdk/configurations/testrail.py +88 -0
  85. alita_sdk/configurations/xray.py +93 -0
  86. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  87. alita_sdk/configurations/zephyr_essential.py +75 -0
  88. alita_sdk/runtime/clients/artifact.py +3 -3
  89. alita_sdk/runtime/clients/client.py +388 -46
  90. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  91. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  92. alita_sdk/runtime/clients/sandbox_client.py +8 -21
  93. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  94. alita_sdk/runtime/langchain/assistant.py +157 -39
  95. alita_sdk/runtime/langchain/constants.py +647 -1
  96. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  97. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
  100. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  102. alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
  103. alita_sdk/runtime/langchain/langraph_agent.py +405 -84
  104. alita_sdk/runtime/langchain/utils.py +106 -7
  105. alita_sdk/runtime/llms/preloaded.py +2 -6
  106. alita_sdk/runtime/models/mcp_models.py +61 -0
  107. alita_sdk/runtime/skills/__init__.py +91 -0
  108. alita_sdk/runtime/skills/callbacks.py +498 -0
  109. alita_sdk/runtime/skills/discovery.py +540 -0
  110. alita_sdk/runtime/skills/executor.py +610 -0
  111. alita_sdk/runtime/skills/input_builder.py +371 -0
  112. alita_sdk/runtime/skills/models.py +330 -0
  113. alita_sdk/runtime/skills/registry.py +355 -0
  114. alita_sdk/runtime/skills/skill_runner.py +330 -0
  115. alita_sdk/runtime/toolkits/__init__.py +31 -0
  116. alita_sdk/runtime/toolkits/application.py +29 -10
  117. alita_sdk/runtime/toolkits/artifact.py +20 -11
  118. alita_sdk/runtime/toolkits/datasource.py +13 -6
  119. alita_sdk/runtime/toolkits/mcp.py +783 -0
  120. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  121. alita_sdk/runtime/toolkits/planning.py +178 -0
  122. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  123. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  124. alita_sdk/runtime/toolkits/tools.py +356 -69
  125. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  126. alita_sdk/runtime/tools/__init__.py +10 -3
  127. alita_sdk/runtime/tools/application.py +27 -6
  128. alita_sdk/runtime/tools/artifact.py +511 -28
  129. alita_sdk/runtime/tools/data_analysis.py +183 -0
  130. alita_sdk/runtime/tools/function.py +67 -35
  131. alita_sdk/runtime/tools/graph.py +10 -4
  132. alita_sdk/runtime/tools/image_generation.py +148 -46
  133. alita_sdk/runtime/tools/llm.py +1003 -128
  134. alita_sdk/runtime/tools/loop.py +3 -1
  135. alita_sdk/runtime/tools/loop_output.py +3 -1
  136. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  137. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  138. alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
  139. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  140. alita_sdk/runtime/tools/planning/models.py +246 -0
  141. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  142. alita_sdk/runtime/tools/router.py +2 -4
  143. alita_sdk/runtime/tools/sandbox.py +65 -48
  144. alita_sdk/runtime/tools/skill_router.py +776 -0
  145. alita_sdk/runtime/tools/tool.py +3 -1
  146. alita_sdk/runtime/tools/vectorstore.py +9 -3
  147. alita_sdk/runtime/tools/vectorstore_base.py +70 -14
  148. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  149. alita_sdk/runtime/utils/constants.py +5 -1
  150. alita_sdk/runtime/utils/mcp_client.py +492 -0
  151. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  152. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  153. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  154. alita_sdk/runtime/utils/serialization.py +155 -0
  155. alita_sdk/runtime/utils/streamlit.py +40 -13
  156. alita_sdk/runtime/utils/toolkit_utils.py +30 -9
  157. alita_sdk/runtime/utils/utils.py +36 -0
  158. alita_sdk/tools/__init__.py +134 -35
  159. alita_sdk/tools/ado/repos/__init__.py +51 -32
  160. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  161. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  162. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  163. alita_sdk/tools/ado/utils.py +1 -18
  164. alita_sdk/tools/ado/wiki/__init__.py +25 -12
  165. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  166. alita_sdk/tools/ado/work_item/__init__.py +26 -13
  167. alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
  168. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  169. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  170. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  171. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  172. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  173. alita_sdk/tools/base/tool.py +5 -1
  174. alita_sdk/tools/base_indexer_toolkit.py +271 -84
  175. alita_sdk/tools/bitbucket/__init__.py +17 -11
  176. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  177. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  178. alita_sdk/tools/browser/__init__.py +5 -4
  179. alita_sdk/tools/carrier/__init__.py +5 -6
  180. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  181. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  182. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  183. alita_sdk/tools/chunkers/__init__.py +3 -1
  184. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  185. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  186. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  187. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  188. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  189. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  190. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  191. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  192. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  193. alita_sdk/tools/code/linter/__init__.py +10 -8
  194. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  195. alita_sdk/tools/code/sonar/__init__.py +11 -8
  196. alita_sdk/tools/code_indexer_toolkit.py +82 -22
  197. alita_sdk/tools/confluence/__init__.py +22 -16
  198. alita_sdk/tools/confluence/api_wrapper.py +107 -30
  199. alita_sdk/tools/confluence/loader.py +14 -2
  200. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  201. alita_sdk/tools/elastic/__init__.py +11 -8
  202. alita_sdk/tools/elitea_base.py +493 -30
  203. alita_sdk/tools/figma/__init__.py +58 -11
  204. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  205. alita_sdk/tools/figma/figma_client.py +73 -0
  206. alita_sdk/tools/figma/toon_tools.py +2748 -0
  207. alita_sdk/tools/github/__init__.py +14 -15
  208. alita_sdk/tools/github/github_client.py +224 -100
  209. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  210. alita_sdk/tools/github/schemas.py +14 -5
  211. alita_sdk/tools/github/tool.py +5 -1
  212. alita_sdk/tools/github/tool_prompts.py +9 -22
  213. alita_sdk/tools/gitlab/__init__.py +16 -11
  214. alita_sdk/tools/gitlab/api_wrapper.py +218 -48
  215. alita_sdk/tools/gitlab_org/__init__.py +10 -9
  216. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  217. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  218. alita_sdk/tools/google/bigquery/tool.py +5 -1
  219. alita_sdk/tools/google_places/__init__.py +11 -8
  220. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  221. alita_sdk/tools/jira/__init__.py +17 -10
  222. alita_sdk/tools/jira/api_wrapper.py +92 -41
  223. alita_sdk/tools/keycloak/__init__.py +11 -8
  224. alita_sdk/tools/localgit/__init__.py +9 -3
  225. alita_sdk/tools/localgit/local_git.py +62 -54
  226. alita_sdk/tools/localgit/tool.py +5 -1
  227. alita_sdk/tools/memory/__init__.py +12 -4
  228. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  229. alita_sdk/tools/ocr/__init__.py +11 -8
  230. alita_sdk/tools/openapi/__init__.py +491 -106
  231. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  232. alita_sdk/tools/openapi/tool.py +20 -0
  233. alita_sdk/tools/pandas/__init__.py +20 -12
  234. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  235. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  236. alita_sdk/tools/postman/__init__.py +10 -9
  237. alita_sdk/tools/pptx/__init__.py +11 -10
  238. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  239. alita_sdk/tools/qtest/__init__.py +31 -11
  240. alita_sdk/tools/qtest/api_wrapper.py +2135 -86
  241. alita_sdk/tools/rally/__init__.py +10 -9
  242. alita_sdk/tools/rally/api_wrapper.py +1 -1
  243. alita_sdk/tools/report_portal/__init__.py +12 -8
  244. alita_sdk/tools/salesforce/__init__.py +10 -8
  245. alita_sdk/tools/servicenow/__init__.py +17 -15
  246. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  247. alita_sdk/tools/sharepoint/__init__.py +10 -7
  248. alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
  249. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  250. alita_sdk/tools/sharepoint/utils.py +8 -2
  251. alita_sdk/tools/slack/__init__.py +10 -7
  252. alita_sdk/tools/slack/api_wrapper.py +2 -2
  253. alita_sdk/tools/sql/__init__.py +12 -9
  254. alita_sdk/tools/testio/__init__.py +10 -7
  255. alita_sdk/tools/testrail/__init__.py +11 -10
  256. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  257. alita_sdk/tools/utils/__init__.py +9 -4
  258. alita_sdk/tools/utils/content_parser.py +103 -18
  259. alita_sdk/tools/utils/text_operations.py +410 -0
  260. alita_sdk/tools/utils/tool_prompts.py +79 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
  262. alita_sdk/tools/xray/__init__.py +13 -9
  263. alita_sdk/tools/yagmail/__init__.py +9 -3
  264. alita_sdk/tools/zephyr/__init__.py +10 -7
  265. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
  266. alita_sdk/tools/zephyr_essential/__init__.py +10 -7
  267. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  268. alita_sdk/tools/zephyr_essential/client.py +2 -2
  269. alita_sdk/tools/zephyr_scale/__init__.py +11 -8
  270. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  271. alita_sdk/tools/zephyr_squad/__init__.py +10 -7
  272. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
  273. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  274. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  275. alita_sdk-0.3.379.dist-info/RECORD +0 -360
  276. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,183 @@
1
+ """
2
+ Data Analysis internal tool for Alita SDK.
3
+
4
+ This tool provides Pandas-based data analysis capabilities as an internal tool,
5
+ accessible through the "Enable internal tools" dropdown menu.
6
+
7
+ It uses the conversation attachment bucket for file storage, providing seamless
8
+ integration with drag-and-drop file uploads in chat.
9
+ """
10
+ import logging
11
+ from typing import Any, List, Literal, Optional, Type
12
+
13
+ from langchain_core.tools import BaseTool, BaseToolkit
14
+ from pydantic import BaseModel, ConfigDict, create_model, Field
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ name = "data_analysis"
19
+
20
+
21
+ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store=None):
22
+ """
23
+ Get data analysis tools for the provided tool configurations.
24
+
25
+ Args:
26
+ tools_list: List of tool configurations
27
+ alita_client: Alita client instance (required for data analysis)
28
+ llm: LLM client instance (required for code generation)
29
+ memory_store: Optional memory store instance (unused)
30
+
31
+ Returns:
32
+ List of data analysis tools
33
+ """
34
+ all_tools = []
35
+
36
+ for tool in tools_list:
37
+ if (tool.get('type') == 'data_analysis' or
38
+ tool.get('toolkit_name') == 'data_analysis'):
39
+ try:
40
+ if not alita_client:
41
+ logger.error("Alita client is required for data analysis tools")
42
+ continue
43
+
44
+ settings = tool.get('settings', {})
45
+ bucket_name = settings.get('bucket_name')
46
+
47
+ if not bucket_name:
48
+ logger.error("bucket_name is required for data analysis tools")
49
+ continue
50
+
51
+ toolkit_instance = DataAnalysisToolkit.get_toolkit(
52
+ alita_client=alita_client,
53
+ llm=llm,
54
+ bucket_name=bucket_name,
55
+ toolkit_name=tool.get('toolkit_name', '')
56
+ )
57
+ all_tools.extend(toolkit_instance.get_tools())
58
+ except Exception as e:
59
+ logger.error(f"Error in data analysis toolkit get_tools: {e}")
60
+ logger.error(f"Tool config: {tool}")
61
+ raise
62
+
63
+ return all_tools
64
+
65
+
66
+ class DataAnalysisToolkit(BaseToolkit):
67
+ """
68
+ Data Analysis toolkit providing Pandas-based data analysis capabilities.
69
+
70
+ This is an internal tool that uses the conversation attachment bucket
71
+ for file storage, enabling seamless integration with chat file uploads.
72
+ """
73
+ tools: List[BaseTool] = []
74
+
75
+ @staticmethod
76
+ def toolkit_config_schema() -> Type[BaseModel]:
77
+ """Get the configuration schema for the data analysis toolkit."""
78
+ # Import PandasWrapper to get available tools schema
79
+ from alita_sdk.tools.pandas.api_wrapper import PandasWrapper
80
+
81
+ selected_tools = {
82
+ x['name']: x['args_schema'].model_json_schema()
83
+ for x in PandasWrapper.model_construct().get_available_tools()
84
+ }
85
+
86
+ return create_model(
87
+ 'data_analysis',
88
+ bucket_name=(
89
+ Optional[str],
90
+ Field(
91
+ default=None,
92
+ title="Bucket name",
93
+ description="Bucket where files are stored (auto-injected from conversation)"
94
+ )
95
+ ),
96
+ selected_tools=(
97
+ List[Literal[tuple(selected_tools)]],
98
+ Field(
99
+ default=[],
100
+ json_schema_extra={'args_schemas': selected_tools}
101
+ )
102
+ ),
103
+ __config__=ConfigDict(json_schema_extra={
104
+ 'metadata': {
105
+ "label": "Data Analysis",
106
+ "icon_url": "data-analysis.svg",
107
+ "hidden": True, # Hidden from regular toolkit menu
108
+ "categories": ["internal_tool"],
109
+ "extra_categories": ["data analysis", "pandas", "dataframes", "data science"],
110
+ }
111
+ })
112
+ )
113
+
114
+ @classmethod
115
+ def get_toolkit(
116
+ cls,
117
+ alita_client=None,
118
+ llm=None,
119
+ bucket_name: str = None,
120
+ toolkit_name: Optional[str] = None,
121
+ selected_tools: Optional[List[str]] = None,
122
+ **kwargs
123
+ ):
124
+ """
125
+ Get toolkit with data analysis tools.
126
+
127
+ Args:
128
+ alita_client: Alita client instance (required)
129
+ llm: LLM for code generation (optional, uses alita_client.llm if not provided)
130
+ bucket_name: Conversation attachment bucket (required)
131
+ toolkit_name: Optional name prefix for tools
132
+ selected_tools: Optional list of tool names to include (default: all)
133
+ **kwargs: Additional arguments
134
+
135
+ Returns:
136
+ DataAnalysisToolkit instance with configured tools
137
+
138
+ Raises:
139
+ ValueError: If alita_client or bucket_name is not provided
140
+ """
141
+ if not alita_client:
142
+ raise ValueError("Alita client is required for data analysis")
143
+
144
+ if not bucket_name:
145
+ raise ValueError("bucket_name is required for data analysis (should be conversation attachment bucket)")
146
+
147
+ # Import the PandasWrapper from existing toolkit
148
+ from alita_sdk.tools.pandas.api_wrapper import PandasWrapper
149
+ from alita_sdk.tools.base.tool import BaseAction
150
+
151
+ # Create wrapper with conversation bucket
152
+ wrapper = PandasWrapper(
153
+ alita=alita_client,
154
+ llm=llm,
155
+ bucket_name=bucket_name
156
+ )
157
+
158
+ # Get tools from wrapper
159
+ available_tools = wrapper.get_available_tools()
160
+ tools = []
161
+
162
+ for tool in available_tools:
163
+ # Filter by selected_tools if provided
164
+ if selected_tools and tool["name"] not in selected_tools:
165
+ continue
166
+
167
+ description = tool["description"]
168
+ if toolkit_name:
169
+ description = f"Toolkit: {toolkit_name}\n{description}"
170
+ description = description[:1000]
171
+
172
+ tools.append(BaseAction(
173
+ api_wrapper=wrapper,
174
+ name=tool["name"],
175
+ description=description,
176
+ args_schema=tool["args_schema"],
177
+ metadata={"toolkit_name": toolkit_name, "toolkit_type": name} if toolkit_name else {}
178
+ ))
179
+
180
+ return cls(tools=tools)
181
+
182
+ def get_tools(self):
183
+ return self.tools
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import json
2
3
  import logging
3
4
  from copy import deepcopy
@@ -7,15 +8,28 @@ from langchain_core.callbacks import dispatch_custom_event
7
8
  from langchain_core.messages import ToolCall
8
9
  from langchain_core.runnables import RunnableConfig
9
10
  from langchain_core.tools import BaseTool, ToolException
10
- from typing import Any, Optional, Union, Annotated
11
+ from typing import Any, Optional, Union
11
12
  from langchain_core.utils.function_calling import convert_to_openai_tool
12
13
  from pydantic import ValidationError
13
14
 
14
15
  from ..langchain.utils import propagate_the_input_mapping
16
+ from ..utils.serialization import safe_serialize
15
17
 
16
18
  logger = logging.getLogger(__name__)
17
19
 
18
20
 
21
+ def replace_escaped_newlines(data):
22
+ """
23
+ Replace \\n with \n in all string values recursively.
24
+ Required for sanitization of state variables in code node
25
+ """
26
+ if isinstance(data, dict):
27
+ return {key: replace_escaped_newlines(value) for key, value in data.items()}
28
+ elif isinstance(data, str):
29
+ return data.replace('\\n', '\n')
30
+ else:
31
+ return data
32
+
19
33
  class FunctionTool(BaseTool):
20
34
  name: str = 'FunctionalTool'
21
35
  description: str = 'This is direct call node for tools'
@@ -28,31 +42,36 @@ class FunctionTool(BaseTool):
28
42
  alita_client: Optional[Any] = None
29
43
 
30
44
  def _prepare_pyodide_input(self, state: Union[str, dict, ToolCall]) -> str:
31
- """Prepare input for PyodideSandboxTool by injecting state into the code block."""
32
- # add state into the code block here since it might be changed during the execution of the code
33
- state_copy = deepcopy(state)
34
-
35
- del state_copy['messages'] # remove messages to avoid issues with pickling without langchain-core
36
- # inject state into the code block as alita_state variable
37
- pyodide_predata = f"alita_state = {state_copy}"
38
- # add classes related to sandbox client
39
- # read the content of alita_sdk/runtime/cliens/sandbox_client.py
40
- try:
41
- import os
42
- from pathlib import Path
43
-
44
- # Get the directory of the current file and construct the path to sandbox_client.py
45
- current_dir = Path(__file__).parent
46
- sandbox_client_path = current_dir.parent / 'clients' / 'sandbox_client.py'
47
-
48
- with open(sandbox_client_path, 'r') as f:
49
- sandbox_client_code = f.read()
50
- pyodide_predata += f"\n{sandbox_client_code}\n"
51
- pyodide_predata += (f"alita_client = SandboxClient(base_url='{self.alita_client.base_url}',"
52
- f"project_id={self.alita_client.project_id},"
53
- f"auth_token='{self.alita_client.auth_token}')")
54
- except FileNotFoundError:
55
- logger.error(f"sandbox_client.py not found at {sandbox_client_path}. Ensure the file exists.")
45
+ """Prepare input for PyodideSandboxTool by injecting state into the code block.
46
+
47
+ Uses base64 encoding to avoid string escaping issues when passing JSON
48
+ through multiple layers of parsing (Python -> Deno -> Pyodide) and compression to minimize args list
49
+ """
50
+ import base64
51
+ import zlib
52
+
53
+ state_copy = replace_escaped_newlines(deepcopy(state))
54
+ if 'messages' in state_copy:
55
+ del state_copy['messages']
56
+
57
+ # Use safe_serialize to handle Pydantic models, datetime, and other non-JSON types
58
+ state_json = safe_serialize(state_copy)
59
+
60
+ # Use base64 encoding to avoid all string escaping issues
61
+ # This is more robust than repr() when the code passes through multiple parsers
62
+ # use compression to avoid issue with `{"error": "Error executing code: [Errno 7] Argument list too long: 'deno'"}`
63
+ compressed = zlib.compress(state_json.encode('utf-8'))
64
+ encoded = base64.b64encode(compressed).decode('ascii')
65
+
66
+ pyodide_predata = f'''#state dict
67
+ import json
68
+ import base64
69
+ import zlib
70
+
71
+ compressed_state = base64.b64decode('{encoded}')
72
+ state_json = zlib.decompress(compressed_state).decode('utf-8')
73
+ alita_state = json.loads(state_json)
74
+ '''
56
75
  return pyodide_predata
57
76
 
58
77
  def _handle_pyodide_output(self, tool_result: Any) -> dict:
@@ -111,7 +130,8 @@ class FunctionTool(BaseTool):
111
130
 
112
131
  # special handler for PyodideSandboxTool
113
132
  if self._is_pyodide_tool():
114
- code = func_args['code']
133
+ # replace new lines in strings in code block
134
+ code = func_args['code'].replace('\\n', '\\\\n')
115
135
  func_args['code'] = f"{self._prepare_pyodide_input(state)}\n{code}"
116
136
  try:
117
137
  tool_result = self.tool.invoke(func_args, config, **kwargs)
@@ -132,14 +152,26 @@ class FunctionTool(BaseTool):
132
152
  if not self.output_variables:
133
153
  return {"messages": [{"role": "assistant", "content": dumps(tool_result)}]}
134
154
  else:
135
- if self.output_variables[0] == "messages":
136
- return {
137
- "messages": [{
138
- "role": "assistant",
139
- "content": dumps(tool_result) if not isinstance(tool_result, ToolException) else str(
140
- tool_result)
141
- }]
142
- }
155
+ if "messages" in self.output_variables:
156
+ if 'messages' in tool_result:
157
+ # case when the sub-graph has been executed
158
+ messages_dict = {"messages": tool_result['messages']}
159
+ else:
160
+ messages_dict = {
161
+ "messages": [{
162
+ "role": "assistant",
163
+ "content": dumps(tool_result)
164
+ if not isinstance(tool_result, ToolException) and not isinstance(tool_result, str)
165
+ else str(tool_result)
166
+ }]
167
+ }
168
+ for var in self.output_variables:
169
+ if var != "messages":
170
+ if isinstance(tool_result, dict) and var in tool_result:
171
+ messages_dict[var] = tool_result[var]
172
+ else:
173
+ messages_dict[var] = tool_result
174
+ return messages_dict
143
175
  else:
144
176
  return { self.output_variables[0]: tool_result }
145
177
  except ValidationError:
@@ -47,8 +47,8 @@ def formulate_query(kwargs):
47
47
 
48
48
 
49
49
  class GraphTool(BaseTool):
50
- name: str
51
- description: str
50
+ name: str = 'GraphTool'
51
+ description: str = 'Graph tool for tools'
52
52
  graph: CompiledStateGraph
53
53
  args_schema: Type[BaseModel] = graphToolSchema
54
54
  return_type: str = "str"
@@ -65,10 +65,16 @@ class GraphTool(BaseTool):
65
65
  all_kwargs = {**kwargs, **extras, **schema_values}
66
66
  if config is None:
67
67
  config = {}
68
- return self._run(*config, **all_kwargs)
68
+ # Pass the config to the _run empty or the one passed from the parent executor.
69
+ return self._run(config, **all_kwargs)
69
70
 
70
71
  def _run(self, *args, **kwargs):
71
- response = self.graph.invoke(formulate_query(kwargs))
72
+ config = None
73
+ # From invoke method we are passing only 1 arg so it is safe to do this condition and config assignment.
74
+ # Default to None is safe because it will be checked also on the langchain side.
75
+ if args:
76
+ config = args[0]
77
+ response = self.graph.invoke(formulate_query(kwargs), config=config)
72
78
  if self.return_type == "str":
73
79
  return response["output"]
74
80
  else:
@@ -1,17 +1,62 @@
1
1
  """
2
2
  Image generation tool for Alita SDK.
3
3
  """
4
+ import json
4
5
  import logging
5
- from typing import Optional, Type, Any
6
- from langchain_core.tools import BaseTool
7
- from pydantic import BaseModel, Field
6
+ import uuid
7
+ from typing import Optional, Type, Any, List, Literal
8
+ from langchain_core.tools import BaseTool, BaseToolkit
9
+ from pydantic import BaseModel, Field, create_model, ConfigDict
8
10
 
9
11
  logger = logging.getLogger(__name__)
10
12
 
13
+ name = "image_generation"
14
+
15
+
16
+ def get_tools(tools_list: list, alita_client=None, llm=None,
17
+ memory_store=None):
18
+ """
19
+ Get image generation tools for the provided tool configurations.
20
+
21
+ Args:
22
+ tools_list: List of tool configurations
23
+ alita_client: Alita client instance (required for image generation)
24
+ llm: LLM client instance (unused for image generation)
25
+ memory_store: Optional memory store instance (unused)
26
+
27
+ Returns:
28
+ List of image generation tools
29
+ """
30
+ all_tools = []
31
+
32
+ for tool in tools_list:
33
+ if (tool.get('type') == 'image_generation' or
34
+ tool.get('toolkit_name') == 'image_generation'):
35
+ try:
36
+ if not alita_client:
37
+ logger.error("Alita client is required for image "
38
+ "generation tools")
39
+ continue
40
+
41
+ toolkit_instance = ImageGenerationToolkit.get_toolkit(
42
+ client=alita_client,
43
+ toolkit_name=tool.get('toolkit_name', '')
44
+ )
45
+ all_tools.extend(toolkit_instance.get_tools())
46
+ except Exception as e:
47
+ logger.error(f"Error in image generation toolkit "
48
+ f"get_tools: {e}")
49
+ logger.error(f"Tool config: {tool}")
50
+ raise
51
+
52
+ return all_tools
53
+
11
54
 
12
55
  class ImageGenerationInput(BaseModel):
13
56
  """Input schema for image generation tool."""
14
- prompt: str = Field(description="Text prompt describing the image to generate")
57
+ prompt: str = Field(
58
+ description="Text prompt describing the image to generate"
59
+ )
15
60
  n: int = Field(
16
61
  default=1, description="Number of images to generate (1-10)",
17
62
  ge=1, le=10
@@ -22,7 +67,7 @@ class ImageGenerationInput(BaseModel):
22
67
  )
23
68
  quality: str = Field(
24
69
  default="auto",
25
- description="Quality of the generated image ('low', 'medium', 'high', 'auto')"
70
+ description="Quality of the generated image ('low', 'medium', 'high')"
26
71
  )
27
72
  style: Optional[str] = Field(
28
73
  default=None, description="Style of the generated image (optional)"
@@ -33,7 +78,12 @@ class ImageGenerationTool(BaseTool):
33
78
  """Tool for generating images using the Alita client."""
34
79
 
35
80
  name: str = "generate_image"
36
- description: str = "Generate images from text prompts using AI models"
81
+ description: str = (
82
+ "Generate images from text prompts using AI models. "
83
+ "Returns a JSON object with 'cached_image_id' field containing a reference to the generated image data. "
84
+ "The cached_image_id can be used to save or process the image. "
85
+ "The actual image data is stored temporarily and can be retrieved using the cached_image_id reference."
86
+ )
37
87
  args_schema: Type[BaseModel] = ImageGenerationInput
38
88
  alita_client: Any = None
39
89
 
@@ -42,10 +92,10 @@ class ImageGenerationTool(BaseTool):
42
92
  self.alita_client = client
43
93
 
44
94
  def _run(self, prompt: str, n: int = 1, size: str = "auto",
45
- quality: str = "auto", style: Optional[str] = None) -> list:
95
+ quality: str = "auto", style: Optional[str] = None) -> str:
46
96
  """Generate an image based on the provided parameters."""
47
97
  try:
48
- logger.info(f"Generating image with prompt: {prompt[:50]}...")
98
+ logger.debug(f"Generating image with prompt: {prompt[:50]}...")
49
99
 
50
100
  result = self.alita_client.generate_image(
51
101
  prompt=prompt,
@@ -55,54 +105,56 @@ class ImageGenerationTool(BaseTool):
55
105
  style=style
56
106
  )
57
107
 
58
- # Return multimodal content format for LLM consumption
108
+ # Return simple JSON structure with reference ID instead of full base64
59
109
  if 'data' in result:
60
110
  images = result['data']
61
- content_chunks = []
62
111
 
63
- # Add a text description of what was generated
64
- if len(images) == 1:
65
- content_chunks.append({
66
- "type": "text",
67
- "text": f"Generated image for prompt: '{prompt}'"
68
- })
69
- else:
70
- content_chunks.append({
71
- "type": "text",
72
- "text": f"Generated {len(images)} images for prompt: '{prompt}'"
112
+ # Process all images with unified structure
113
+ images_list = []
114
+ for idx, image_data in enumerate(images, 1):
115
+ if not image_data.get('b64_json'):
116
+ continue
117
+
118
+ cached_image_id = f"img_{uuid.uuid4().hex[:12]}"
119
+
120
+ # Store in cache
121
+ if hasattr(self.alita_client, '_generated_images_cache'):
122
+ self.alita_client._generated_images_cache[cached_image_id] = {
123
+ 'base64_data': image_data['b64_json']
124
+ }
125
+ logger.debug(f"Stored generated image in cache with ID: {cached_image_id}")
126
+
127
+ images_list.append({
128
+ "image_number": idx,
129
+ "image_type": "png",
130
+ "cached_image_id": cached_image_id
73
131
  })
74
132
 
75
- # Add image content for each generated image
76
- for image_data in images:
77
- if image_data.get('url'):
78
- content_chunks.append({
79
- "type": "image_url",
80
- "image_url": {
81
- "url": image_data['url']
82
- }
83
- })
84
- elif image_data.get('b64_json'):
85
- content_chunks.append({
86
- "type": "image_url",
87
- "image_url": {
88
- "url": f"data:image/png;base64,{image_data['b64_json']}"
89
- }
90
- })
133
+ if not images_list:
134
+ return json.dumps({
135
+ "status": "error",
136
+ "message": "No base64 image data found"
137
+ })
91
138
 
92
- return content_chunks
139
+ return json.dumps({
140
+ "status": "success",
141
+ "prompt": prompt,
142
+ "total_images": len(images_list),
143
+ "images": images_list
144
+ })
93
145
 
94
- # Fallback to text response if no images in result
95
- return [{
96
- "type": "text",
97
- "text": f"Image generation completed but no images returned: {result}"
98
- }]
146
+ # Fallback to error response if no images in result
147
+ return json.dumps({
148
+ "status": "error",
149
+ "message": f"Image generation completed but no images returned: {result}"
150
+ })
99
151
 
100
152
  except Exception as e:
101
153
  logger.error(f"Error generating image: {e}")
102
- return [{
103
- "type": "text",
104
- "text": f"Error generating image: {str(e)}"
105
- }]
154
+ return json.dumps({
155
+ "status": "error",
156
+ "message": f"Error generating image: {str(e)}"
157
+ })
106
158
 
107
159
  async def _arun(self, prompt: str, n: int = 1, size: str = "256x256",
108
160
  quality: str = "auto",
@@ -114,3 +166,53 @@ class ImageGenerationTool(BaseTool):
114
166
  def create_image_generation_tool(client):
115
167
  """Create an image generation tool with the provided Alita client."""
116
168
  return ImageGenerationTool(client=client)
169
+
170
+
171
+ class ImageGenerationToolkit(BaseToolkit):
172
+ """Toolkit for image generation tools."""
173
+ tools: List[BaseTool] = []
174
+
175
+ @staticmethod
176
+ def toolkit_config_schema() -> BaseModel:
177
+ """Get the configuration schema for the image generation toolkit."""
178
+ # Create sample tool to get schema
179
+ sample_tool = ImageGenerationTool(client=None)
180
+ selected_tools = {sample_tool.name: sample_tool.args_schema.schema()}
181
+
182
+ return create_model(
183
+ 'image_generation',
184
+ selected_tools=(
185
+ List[Literal[tuple(selected_tools)]],
186
+ Field(
187
+ default=[],
188
+ json_schema_extra={'args_schemas': selected_tools}
189
+ )
190
+ ),
191
+ __config__=ConfigDict(json_schema_extra={
192
+ 'metadata': {
193
+ "label": "Image Generation",
194
+ "icon_url": "image_generation.svg",
195
+ "hidden": True,
196
+ "categories": ["internal_tool"],
197
+ "extra_categories": ["image generation"],
198
+ }
199
+ })
200
+ )
201
+
202
+ @classmethod
203
+ def get_toolkit(cls, client=None, **kwargs):
204
+ """
205
+ Get toolkit with image generation tools.
206
+
207
+ Args:
208
+ client: Alita client instance (required)
209
+ **kwargs: Additional arguments
210
+ """
211
+ if not client:
212
+ raise ValueError("Alita client is required for image generation")
213
+
214
+ tools = [ImageGenerationTool(client=client)]
215
+ return cls(tools=tools)
216
+
217
+ def get_tools(self):
218
+ return self.tools