Files
socweb/backend/app/tools/sandbox.py
T
gongzhiyongandClaude Sonnet 4.6 47921d1a45 fix: resolve 6 gen-ui issues — timeline completeness, regen sync, localStorage persistence
## Fixes
- [#3] Status events now add/update a status ActivityNode in timeline (upsertWsStatusNode)
- [#4] Done callback adds done node; error callback adds error node + sets session.status="error"
- [#5] handleRegenerate fully synced with workspace: status/tool/card/done/error all handled
- [#1][#2] localStorage persistence: completed/error sessions auto-saved, lazy-loaded on demand
- [#1] handleSelectConversation preloads workspace sessions from localStorage for history messages
- Refactored completeWsSession to include done ActivityNode in timeline
- Added errorWsSession, upsertWsStatusNode, saveWsToStorage, loadWsFromStorage helpers

## Known limitation
- [#7] workspace_card merge:true not yet used by backend (all cards are append-only for now)
- History workspace recovery depends on localStorage (browser-local, not cross-device)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 03:08:23 +08:00

75 lines
2.3 KiB
Python

"""Sandboxed code execution tool using Daytona SDK.
Creates an ephemeral Daytona sandbox, runs user code, captures
stdout/stderr, and destroys the sandbox on completion.
"""
from __future__ import annotations
import logging
from langchain_core.tools import tool
from app.config import settings
logger = logging.getLogger(__name__)
@tool
async def sandbox_run(code: str, language: str = "python") -> str:
"""Execute code in a secure sandbox and return the output.
Use this tool when the user asks you to run, execute, or test code.
The sandbox is isolated and ephemeral — it is destroyed after execution.
Supported languages: python, javascript, typescript, bash/shell.
Args:
code: The source code to execute.
language: Programming language (default: python).
"""
# Lazy import so the app starts even if daytona is not installed
try:
from daytona import Daytona, DaytonaConfig, DaytonaError
except ImportError:
return "Sandbox execution is not available: the daytona SDK is not installed."
logger.info("Sandbox run: language=%s, code length=%d", language, len(code))
config = DaytonaConfig(
api_key=settings.daytona_api_key,
api_url=settings.daytona_api_url,
target="us",
)
daytona = Daytona(config)
sandbox = None
try:
sandbox = daytona.create()
if language in ("bash", "shell", "sh"):
response = sandbox.process.exec(code, timeout=30)
else:
response = sandbox.process.code_run(code, timeout=30)
exit_code = getattr(response, "exit_code", None)
result = getattr(response, "result", str(response))
# Truncate very long output
if len(result) > 10000:
result = result[:10000] + "\n... (output truncated)"
if exit_code and exit_code != 0:
return f"[Exit code: {exit_code}]\n{result}"
return result or "(no output)"
except Exception as exc:
logger.exception("Sandbox execution failed")
return f"Sandbox execution failed: {exc}"
finally:
if sandbox is not None:
try:
daytona.remove(sandbox)
except Exception:
logger.warning("Failed to remove sandbox", exc_info=True)