alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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 +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3601 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +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 +111 -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 +407 -92
  110. alita_sdk/runtime/langchain/utils.py +102 -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 +24 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +780 -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 +1013 -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/mcp_client.py +492 -0
  155. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  156. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  157. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  158. alita_sdk/runtime/utils/streamlit.py +41 -14
  159. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  160. alita_sdk/runtime/utils/utils.py +48 -0
  161. alita_sdk/tools/__init__.py +135 -37
  162. alita_sdk/tools/ado/__init__.py +2 -2
  163. alita_sdk/tools/ado/repos/__init__.py +15 -19
  164. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  165. alita_sdk/tools/ado/test_plan/__init__.py +26 -8
  166. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  167. alita_sdk/tools/ado/wiki/__init__.py +27 -12
  168. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  169. alita_sdk/tools/ado/work_item/__init__.py +27 -12
  170. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  171. alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
  172. alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
  173. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  174. alita_sdk/tools/azure_ai/search/__init__.py +13 -8
  175. alita_sdk/tools/base/tool.py +5 -1
  176. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  177. alita_sdk/tools/bitbucket/__init__.py +27 -19
  178. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  179. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  180. alita_sdk/tools/browser/__init__.py +41 -16
  181. alita_sdk/tools/browser/crawler.py +3 -1
  182. alita_sdk/tools/browser/utils.py +15 -6
  183. alita_sdk/tools/carrier/__init__.py +18 -17
  184. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  185. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  186. alita_sdk/tools/chunkers/__init__.py +3 -1
  187. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  188. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  189. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  190. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  191. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  192. alita_sdk/tools/cloud/aws/__init__.py +11 -7
  193. alita_sdk/tools/cloud/azure/__init__.py +11 -7
  194. alita_sdk/tools/cloud/gcp/__init__.py +11 -7
  195. alita_sdk/tools/cloud/k8s/__init__.py +11 -7
  196. alita_sdk/tools/code/linter/__init__.py +9 -8
  197. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  198. alita_sdk/tools/code/sonar/__init__.py +20 -13
  199. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  200. alita_sdk/tools/confluence/__init__.py +21 -14
  201. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  202. alita_sdk/tools/confluence/loader.py +14 -2
  203. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  204. alita_sdk/tools/elastic/__init__.py +10 -8
  205. alita_sdk/tools/elitea_base.py +546 -64
  206. alita_sdk/tools/figma/__init__.py +11 -8
  207. alita_sdk/tools/figma/api_wrapper.py +352 -153
  208. alita_sdk/tools/github/__init__.py +17 -17
  209. alita_sdk/tools/github/api_wrapper.py +9 -26
  210. alita_sdk/tools/github/github_client.py +81 -12
  211. alita_sdk/tools/github/schemas.py +2 -1
  212. alita_sdk/tools/github/tool.py +5 -1
  213. alita_sdk/tools/gitlab/__init__.py +18 -13
  214. alita_sdk/tools/gitlab/api_wrapper.py +224 -80
  215. alita_sdk/tools/gitlab_org/__init__.py +13 -10
  216. alita_sdk/tools/google/bigquery/__init__.py +13 -13
  217. alita_sdk/tools/google/bigquery/tool.py +5 -1
  218. alita_sdk/tools/google_places/__init__.py +20 -11
  219. alita_sdk/tools/jira/__init__.py +21 -11
  220. alita_sdk/tools/jira/api_wrapper.py +315 -168
  221. alita_sdk/tools/keycloak/__init__.py +10 -8
  222. alita_sdk/tools/localgit/__init__.py +8 -3
  223. alita_sdk/tools/localgit/local_git.py +62 -54
  224. alita_sdk/tools/localgit/tool.py +5 -1
  225. alita_sdk/tools/memory/__init__.py +38 -14
  226. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  227. alita_sdk/tools/ocr/__init__.py +10 -8
  228. alita_sdk/tools/openapi/__init__.py +281 -108
  229. alita_sdk/tools/openapi/api_wrapper.py +883 -0
  230. alita_sdk/tools/openapi/tool.py +20 -0
  231. alita_sdk/tools/pandas/__init__.py +18 -11
  232. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  233. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  234. alita_sdk/tools/postman/__init__.py +10 -11
  235. alita_sdk/tools/postman/api_wrapper.py +19 -8
  236. alita_sdk/tools/postman/postman_analysis.py +8 -1
  237. alita_sdk/tools/pptx/__init__.py +10 -10
  238. alita_sdk/tools/qtest/__init__.py +21 -14
  239. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  240. alita_sdk/tools/rally/__init__.py +12 -10
  241. alita_sdk/tools/report_portal/__init__.py +22 -16
  242. alita_sdk/tools/salesforce/__init__.py +21 -16
  243. alita_sdk/tools/servicenow/__init__.py +20 -16
  244. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  245. alita_sdk/tools/sharepoint/__init__.py +16 -14
  246. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  247. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  248. alita_sdk/tools/sharepoint/utils.py +8 -2
  249. alita_sdk/tools/slack/__init__.py +11 -7
  250. alita_sdk/tools/sql/__init__.py +21 -19
  251. alita_sdk/tools/sql/api_wrapper.py +71 -23
  252. alita_sdk/tools/testio/__init__.py +20 -13
  253. alita_sdk/tools/testrail/__init__.py +12 -11
  254. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  255. alita_sdk/tools/utils/__init__.py +28 -4
  256. alita_sdk/tools/utils/content_parser.py +182 -62
  257. alita_sdk/tools/utils/text_operations.py +254 -0
  258. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  259. alita_sdk/tools/xray/__init__.py +17 -14
  260. alita_sdk/tools/xray/api_wrapper.py +58 -113
  261. alita_sdk/tools/yagmail/__init__.py +8 -3
  262. alita_sdk/tools/zephyr/__init__.py +11 -7
  263. alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
  264. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  265. alita_sdk/tools/zephyr_essential/__init__.py +15 -10
  266. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  267. alita_sdk/tools/zephyr_essential/client.py +6 -4
  268. alita_sdk/tools/zephyr_scale/__init__.py +12 -8
  269. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  270. alita_sdk/tools/zephyr_squad/__init__.py +11 -7
  271. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
  272. alita_sdk-0.3.562.dist-info/RECORD +450 -0
  273. alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
  274. alita_sdk/tools/bitbucket/tools.py +0 -304
  275. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  276. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,375 @@
1
+ import asyncio
2
+ import logging
3
+ import subprocess
4
+ import os
5
+ from typing import Any, Type, Optional, Dict, List, Literal, Union
6
+ from copy import deepcopy
7
+ from pathlib import Path
8
+
9
+ from langchain_core.tools import BaseTool, BaseToolkit
10
+ from langchain_core.messages import ToolCall
11
+ from pydantic import BaseModel, create_model, ConfigDict, Field
12
+ from pydantic.fields import FieldInfo
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ name = "pyodide"
17
+
18
+
19
+ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store=None):
20
+ """
21
+ Get sandbox tools for the provided tool configurations.
22
+
23
+ Args:
24
+ tools_list: List of tool configurations
25
+ alita_client: Alita client instance for sandbox tools
26
+ llm: LLM client instance (unused for sandbox)
27
+ memory_store: Optional memory store instance (unused for sandbox)
28
+
29
+ Returns:
30
+ List of sandbox tools
31
+ """
32
+ all_tools = []
33
+
34
+ for tool in tools_list:
35
+ if tool.get('type') == 'sandbox' or tool.get('toolkit_name') == 'sandbox':
36
+ try:
37
+ toolkit_instance = SandboxToolkit.get_toolkit(
38
+ stateful=tool['settings'].get('stateful', False),
39
+ allow_net=tool['settings'].get('allow_net', True),
40
+ alita_client=alita_client,
41
+ toolkit_name=tool.get('toolkit_name', '')
42
+ )
43
+ all_tools.extend(toolkit_instance.get_tools())
44
+ except Exception as e:
45
+ logger.error(f"Error in sandbox toolkit get_tools: {e}")
46
+ logger.error(f"Tool config: {tool}")
47
+ raise
48
+
49
+ return all_tools
50
+
51
+
52
+ def _is_deno_available() -> bool:
53
+ """Check if Deno is available in the PATH"""
54
+ try:
55
+ result = subprocess.run(
56
+ ["deno", "--version"],
57
+ capture_output=True,
58
+ text=True,
59
+ timeout=10
60
+ )
61
+ return result.returncode == 0
62
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
63
+ return False
64
+
65
+
66
+ def _setup_pyodide_cache_env() -> None:
67
+ """Setup Pyodide caching environment variables for performance optimization [NO-OP]"""
68
+ try:
69
+ for key in ["SANDBOX_BASE", "DENO_DIR"]:
70
+ logger.info("Sandbox env: %s -> %s", key, os.environ.get(key, "n/a"))
71
+ except Exception as e:
72
+ logger.warning(f"Could not setup Pyodide cache environment: {e}")
73
+
74
+
75
+ # Create input schema for the sandbox tool
76
+ sandbox_tool_input = create_model(
77
+ "SandboxToolInput",
78
+ code=(str, FieldInfo(description="Python code to execute in the sandbox environment"))
79
+ )
80
+
81
+
82
+ class PyodideSandboxTool(BaseTool):
83
+ """
84
+ A tool that provides secure Python code execution using Pyodide (Python compiled to WebAssembly).
85
+ This tool leverages langchain-sandbox to provide a safe environment for running untrusted Python code.
86
+ Optimized for performance with caching and stateless execution by default.
87
+ """
88
+
89
+ name: str = "pyodide_sandbox"
90
+ description: str = """Execute Python code in a secure sandbox environment using Pyodide.
91
+ This tool allows safe execution of Python code without access to the host system.
92
+ Use this tool when you need to:
93
+ - Execute Python code snippets
94
+ - Perform calculations or data analysis
95
+ - Test Python algorithms
96
+ - Run code that requires isolation from the host system
97
+
98
+ The sandbox supports most Python standard library modules and can install additional packages.
99
+ Note: File access and some system operations are restricted for security.
100
+ Optimized for performance with local caching (stateless by default for faster execution).
101
+ """
102
+ args_schema: Type[BaseModel] = sandbox_tool_input
103
+ stateful: bool = False # Default to stateless for better performance
104
+ allow_net: bool = True
105
+ session_bytes: Optional[bytes] = None
106
+ session_metadata: Optional[Dict] = None
107
+ alita_client: Optional[Any] = None
108
+
109
+ def __init__(self, **kwargs: Any) -> None:
110
+ super().__init__(**kwargs)
111
+ self._sandbox = None
112
+ # Setup caching environment for optimal performance
113
+ _setup_pyodide_cache_env()
114
+ self._initialize_sandbox()
115
+
116
+ def _prepare_pyodide_input(self, code: str) -> str:
117
+ """Prepare input for PyodideSandboxTool by injecting state and alita_client into the code block."""
118
+ pyodide_predata = ""
119
+
120
+ # Add alita_client if available
121
+ if self.alita_client:
122
+ try:
123
+ # Get the directory of the current file and construct the path to sandbox_client.py
124
+ current_dir = Path(__file__).parent
125
+ sandbox_client_path = current_dir.parent / 'clients' / 'sandbox_client.py'
126
+
127
+ with open(sandbox_client_path, 'r') as f:
128
+ sandbox_client_code = f.read()
129
+ pyodide_predata += f"{sandbox_client_code}\n"
130
+ pyodide_predata += (f"alita_client = SandboxClient(base_url='{self.alita_client.base_url}',"
131
+ f"project_id={self.alita_client.project_id},"
132
+ f"auth_token='{self.alita_client.auth_token}')\n")
133
+ except FileNotFoundError:
134
+ logger.error(f"sandbox_client.py not found. Ensure the file exists.")
135
+
136
+ return f"#elitea simplified client\n{pyodide_predata}{code}"
137
+
138
+ def _initialize_sandbox(self) -> None:
139
+ """Initialize the PyodideSandbox instance with optimized settings"""
140
+ try:
141
+ # Check if Deno is available
142
+ if not _is_deno_available():
143
+ error_msg = (
144
+ "Deno is required for PyodideSandbox but is not installed. "
145
+ "Please run the bootstrap.sh script or install Deno manually."
146
+ )
147
+ logger.error(error_msg)
148
+ raise RuntimeError(error_msg)
149
+
150
+ from langchain_sandbox import PyodideSandbox
151
+
152
+ # Air-gapped settings
153
+ sandbox_base = os.environ.get("SANDBOX_BASE", os.path.expanduser('~/.cache/pyodide'))
154
+ sandbox_tmp = os.path.join(sandbox_base, "tmp")
155
+ deno_cache = os.environ.get("DENO_DIR", os.path.expanduser('~/.cache/deno'))
156
+
157
+ # Configure sandbox with performance optimizations
158
+ self._sandbox = PyodideSandbox(
159
+ stateful=self.stateful,
160
+ #
161
+ allow_env=["SANDBOX_BASE"],
162
+ allow_read=[sandbox_base, sandbox_tmp, deno_cache],
163
+ allow_write=[sandbox_tmp, deno_cache],
164
+ #
165
+ allow_net=self.allow_net,
166
+ # Use auto node_modules_dir for better caching
167
+ node_modules_dir="auto"
168
+ )
169
+ logger.info(f"PyodideSandbox initialized successfully (stateful={self.stateful})")
170
+ except ImportError as e:
171
+ if "langchain_sandbox" in str(e):
172
+ error_msg = (
173
+ "langchain-sandbox is required for the PyodideSandboxTool. "
174
+ "Please install it with: pip install langchain-sandbox"
175
+ )
176
+ logger.error(error_msg)
177
+ raise ImportError(error_msg) from e
178
+ else:
179
+ logger.error(f"Failed to import required module: {e}")
180
+ raise
181
+ except Exception as e:
182
+ logger.error(f"Failed to initialize PyodideSandbox: {e}")
183
+ raise
184
+
185
+ def _run(self, code: str) -> str:
186
+ """
187
+ Synchronous version - runs the async method in a new event loop
188
+ """
189
+ try:
190
+ # Check if sandbox is initialized, if not try to initialize
191
+ if self._sandbox is None:
192
+ self._initialize_sandbox()
193
+
194
+ # Prepare code with state and client injection
195
+ prepared_code = self._prepare_pyodide_input(code)
196
+
197
+ # Check if we're already in an async context
198
+ try:
199
+ loop = asyncio.get_running_loop()
200
+ # We're in an async context, but _run is supposed to be sync
201
+ # We'll need to use a different approach
202
+ import concurrent.futures
203
+ with concurrent.futures.ThreadPoolExecutor() as executor:
204
+ future = executor.submit(asyncio.run, self._arun(prepared_code))
205
+ return future.result()
206
+ except RuntimeError:
207
+ # No running loop, safe to use asyncio.run
208
+ return asyncio.run(self._arun(prepared_code))
209
+ except (ImportError, RuntimeError) as e:
210
+ # Handle specific dependency errors gracefully
211
+ error_msg = str(e)
212
+ if "langchain-sandbox" in error_msg:
213
+ return "❌ PyodideSandboxTool requires langchain-sandbox. Install with: pip install langchain-sandbox"
214
+ elif "Deno" in error_msg:
215
+ return "❌ PyodideSandboxTool requires Deno. Install from: https://docs.deno.com/runtime/getting_started/installation/"
216
+ else:
217
+ return f"❌ PyodideSandboxTool initialization failed: {error_msg}"
218
+ except Exception as e:
219
+ logger.error(f"Error executing code in sandbox: {e}")
220
+ return f"Error executing code: {str(e)}"
221
+
222
+ async def _arun(self, code: str) -> str:
223
+ """
224
+ Execute Python code in the Pyodide sandbox
225
+ """
226
+ try:
227
+ if self._sandbox is None:
228
+ self._initialize_sandbox()
229
+
230
+ # Execute the code with session state if available
231
+ result = await self._sandbox.execute(
232
+ code,
233
+ session_bytes=self.session_bytes,
234
+ session_metadata=self.session_metadata
235
+ )
236
+
237
+ # Update session state for stateful execution
238
+ if self.stateful:
239
+ self.session_bytes = result.session_bytes
240
+ self.session_metadata = result.session_metadata
241
+
242
+ result_dict = {}
243
+
244
+ if result.result is not None:
245
+ result_dict["result"] = result.result
246
+
247
+ if result.stdout:
248
+ result_dict["output"] = result.stdout
249
+
250
+ if result.stderr:
251
+ result_dict["error"] = result.stderr
252
+
253
+ if result.status == 'error':
254
+ result_dict["status"] = "Execution failed"
255
+
256
+ execution_info = f"Execution time: {result.execution_time:.2f}s"
257
+ if result.session_metadata and 'packages' in result.session_metadata:
258
+ packages = result.session_metadata.get('packages', [])
259
+ if packages:
260
+ execution_info += f", Packages: {', '.join(packages)}"
261
+
262
+ result_dict["execution_info"] = execution_info
263
+ return result_dict
264
+
265
+ except Exception as e:
266
+ logger.error(f"Error executing code in sandbox: {e}")
267
+ return {"error": f"Error executing code: {str(e)}"}
268
+
269
+
270
+ class StatefulPyodideSandboxTool(PyodideSandboxTool):
271
+ """
272
+ A stateful version of the PyodideSandboxTool that maintains state between executions.
273
+ This version preserves variables, imports, and function definitions across multiple tool calls.
274
+ """
275
+
276
+ name: str = "stateful_pyodide_sandbox"
277
+ description: str = """Execute Python code in a stateful sandbox environment using Pyodide.
278
+ This tool maintains state between executions, preserving variables, imports, and function definitions.
279
+ Use this tool when you need to:
280
+ - Build upon previous code executions
281
+ - Maintain variables across multiple calls
282
+ - Develop complex programs step by step
283
+ - Preserve imported libraries and defined functions
284
+
285
+ The sandbox supports most Python standard library modules and can install additional packages.
286
+ Note: File access and some system operations are restricted for security.
287
+ """
288
+
289
+ def __init__(self, **kwargs: Any) -> None:
290
+ kwargs['stateful'] = True # Force stateful mode
291
+ super().__init__(**kwargs)
292
+
293
+
294
+ # Factory function for creating sandbox tools
295
+ def create_sandbox_tool(stateful: bool = False, allow_net: bool = True, alita_client: Optional[Any] = None) -> BaseTool:
296
+ """
297
+ Factory function to create sandbox tools with specified configuration.
298
+
299
+ Note: This tool requires Deno to be installed and available in PATH.
300
+ For installation and optimization, run the bootstrap.sh script.
301
+
302
+ Args:
303
+ stateful: Whether to maintain state between executions (default: False for better performance)
304
+ allow_net: Whether to allow network access (for package installation)
305
+
306
+ Returns:
307
+ Configured sandbox tool instance
308
+
309
+ Raises:
310
+ ImportError: If langchain-sandbox is not installed
311
+ RuntimeError: If Deno is not found in PATH
312
+
313
+ Performance Notes:
314
+ - Stateless mode (default) is faster and avoids session state overhead
315
+ - Run bootstrap.sh script to enable local caching and reduce initialization time
316
+ - Cached wheels reduce package download time from ~4.76s to near-instant
317
+ """
318
+ if stateful:
319
+ return StatefulPyodideSandboxTool(allow_net=allow_net, alita_client=alita_client)
320
+ else:
321
+ return PyodideSandboxTool(stateful=False, allow_net=allow_net, alita_client=alita_client)
322
+
323
+
324
+ class SandboxToolkit(BaseToolkit):
325
+ tools: List[BaseTool] = []
326
+
327
+ @staticmethod
328
+ def toolkit_config_schema() -> Type[BaseModel]:
329
+ # Create sample tools to get their schemas
330
+ sample_tools = [
331
+ PyodideSandboxTool(),
332
+ StatefulPyodideSandboxTool()
333
+ ]
334
+ selected_tools = {x.name: x.args_schema.model_json_schema() for x in sample_tools}
335
+
336
+ return create_model(
337
+ 'sandbox',
338
+ stateful=(bool, Field(default=False, description="Whether to maintain state between executions")),
339
+ allow_net=(bool, Field(default=True, description="Whether to allow network access for package installation")),
340
+ selected_tools=(List[Literal[tuple(selected_tools)]],
341
+ Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
342
+
343
+ __config__=ConfigDict(json_schema_extra={
344
+ 'metadata': {
345
+ "label": "Python Sandbox",
346
+ "icon_url": "sandbox.svg",
347
+ "hidden": False,
348
+ "categories": ["code", "execution", "internal_tool"],
349
+ "extra_categories": ["python", "pyodide", "sandbox", "code execution"],
350
+ }
351
+ })
352
+ )
353
+
354
+ @classmethod
355
+ def get_toolkit(cls, stateful: bool = False, allow_net: bool = True, alita_client=None, **kwargs):
356
+ """
357
+ Get toolkit with sandbox tools.
358
+
359
+ Args:
360
+ stateful: Whether to maintain state between executions
361
+ allow_net: Whether to allow network access
362
+ alita_client: Alita client instance for sandbox tools
363
+ **kwargs: Additional arguments
364
+ """
365
+ tools = []
366
+
367
+ if stateful:
368
+ tools.append(StatefulPyodideSandboxTool(allow_net=allow_net, alita_client=alita_client))
369
+ else:
370
+ tools.append(PyodideSandboxTool(stateful=False, allow_net=allow_net, alita_client=alita_client))
371
+
372
+ return cls(tools=tools)
373
+
374
+ def get_tools(self):
375
+ return self.tools