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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +155 -0
- alita_sdk/cli/agent_loader.py +215 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3794 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1073 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +72 -12
- alita_sdk/community/inventory/__init__.py +236 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +173 -0
- alita_sdk/community/inventory/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +11 -0
- alita_sdk/configurations/ado.py +148 -2
- alita_sdk/configurations/azure_search.py +1 -1
- alita_sdk/configurations/bigquery.py +1 -1
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/browser.py +18 -0
- alita_sdk/configurations/carrier.py +19 -0
- alita_sdk/configurations/confluence.py +130 -1
- alita_sdk/configurations/delta_lake.py +1 -1
- alita_sdk/configurations/figma.py +76 -5
- alita_sdk/configurations/github.py +65 -1
- alita_sdk/configurations/gitlab.py +81 -0
- alita_sdk/configurations/google_places.py +17 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +323 -0
- alita_sdk/configurations/postman.py +1 -1
- alita_sdk/configurations/qtest.py +72 -3
- alita_sdk/configurations/report_portal.py +115 -0
- alita_sdk/configurations/salesforce.py +19 -0
- alita_sdk/configurations/service_now.py +1 -12
- alita_sdk/configurations/sharepoint.py +167 -0
- alita_sdk/configurations/sonar.py +18 -0
- alita_sdk/configurations/sql.py +20 -0
- alita_sdk/configurations/testio.py +101 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +94 -1
- alita_sdk/configurations/zephyr_enterprise.py +94 -1
- alita_sdk/configurations/zephyr_essential.py +95 -0
- alita_sdk/runtime/clients/artifact.py +21 -4
- alita_sdk/runtime/clients/client.py +458 -67
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +352 -0
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +183 -43
- alita_sdk/runtime/langchain/constants.py +647 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
- alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
- alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
- alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
- alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
- alita_sdk/runtime/langchain/langraph_agent.py +493 -105
- alita_sdk/runtime/langchain/utils.py +118 -8
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +28 -0
- alita_sdk/runtime/toolkits/application.py +14 -4
- alita_sdk/runtime/toolkits/artifact.py +25 -9
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +782 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +11 -6
- alita_sdk/runtime/toolkits/tools.py +314 -70
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +24 -0
- alita_sdk/runtime/tools/application.py +16 -4
- alita_sdk/runtime/tools/artifact.py +367 -33
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +100 -4
- alita_sdk/runtime/tools/graph.py +81 -0
- alita_sdk/runtime/tools/image_generation.py +218 -0
- alita_sdk/runtime/tools/llm.py +1032 -177
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/router.py +2 -1
- alita_sdk/runtime/tools/sandbox.py +375 -0
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +69 -65
- alita_sdk/runtime/tools/vectorstore_base.py +163 -90
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/constants.py +5 -1
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/streamlit.py +41 -14
- alita_sdk/runtime/utils/toolkit_utils.py +28 -9
- alita_sdk/runtime/utils/utils.py +48 -0
- alita_sdk/tools/__init__.py +135 -37
- alita_sdk/tools/ado/__init__.py +2 -2
- alita_sdk/tools/ado/repos/__init__.py +16 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +27 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +28 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +28 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +14 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +28 -19
- alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
- alita_sdk/tools/browser/__init__.py +41 -16
- alita_sdk/tools/browser/crawler.py +3 -1
- alita_sdk/tools/browser/utils.py +15 -6
- alita_sdk/tools/carrier/__init__.py +18 -17
- alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
- alita_sdk/tools/carrier/excel_reporter.py +8 -4
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/codeparser.py +1 -1
- alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +12 -7
- alita_sdk/tools/cloud/azure/__init__.py +12 -7
- alita_sdk/tools/cloud/gcp/__init__.py +12 -7
- alita_sdk/tools/cloud/k8s/__init__.py +12 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +21 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +22 -14
- alita_sdk/tools/confluence/api_wrapper.py +197 -58
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +60 -11
- alita_sdk/tools/figma/api_wrapper.py +1400 -167
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +18 -17
- alita_sdk/tools/github/api_wrapper.py +9 -26
- alita_sdk/tools/github/github_client.py +81 -12
- alita_sdk/tools/github/schemas.py +2 -1
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/gitlab/__init__.py +19 -13
- alita_sdk/tools/gitlab/api_wrapper.py +256 -80
- alita_sdk/tools/gitlab_org/__init__.py +14 -10
- alita_sdk/tools/google/bigquery/__init__.py +14 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +21 -11
- alita_sdk/tools/jira/__init__.py +22 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +38 -14
- alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
- alita_sdk/tools/ocr/__init__.py +11 -8
- alita_sdk/tools/openapi/__init__.py +491 -106
- alita_sdk/tools/openapi/api_wrapper.py +1357 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- alita_sdk/tools/pandas/api_wrapper.py +40 -45
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +11 -11
- alita_sdk/tools/postman/api_wrapper.py +19 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +11 -10
- alita_sdk/tools/qtest/__init__.py +22 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +13 -10
- alita_sdk/tools/report_portal/__init__.py +23 -16
- alita_sdk/tools/salesforce/__init__.py +22 -16
- alita_sdk/tools/servicenow/__init__.py +21 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +17 -14
- alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +13 -8
- alita_sdk/tools/sql/__init__.py +22 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +21 -13
- alita_sdk/tools/testrail/__init__.py +13 -11
- alita_sdk/tools/testrail/api_wrapper.py +214 -46
- alita_sdk/tools/utils/__init__.py +28 -4
- alita_sdk/tools/utils/content_parser.py +241 -55
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +18 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +12 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +16 -10
- alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
- alita_sdk/tools/zephyr_essential/client.py +6 -4
- alita_sdk/tools/zephyr_scale/__init__.py +13 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +12 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
- alita_sdk-0.3.584.dist-info/RECORD +452 -0
- alita_sdk-0.3.584.dist-info/entry_points.txt +2 -0
- alita_sdk/tools/bitbucket/tools.py +0 -304
- alita_sdk-0.3.257.dist-info/RECORD +0 -343
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.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
|