mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-22 23:31:12 +02:00
make execute_code a Python tool: auto-wrap, print() nudge on empty output
This commit is contained in:
parent
5719a794a0
commit
5ea2732bf2
1 changed files with 25 additions and 12 deletions
|
|
@ -138,21 +138,22 @@ SURFSENSE_GREP_TOOL_DESCRIPTION = """Search for a literal text pattern across fi
|
||||||
Use this to locate relevant document files/chunks before reading full files.
|
Use this to locate relevant document files/chunks before reading full files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SURFSENSE_EXECUTE_CODE_TOOL_DESCRIPTION = """Executes a shell command in an isolated code execution environment.
|
SURFSENSE_EXECUTE_CODE_TOOL_DESCRIPTION = """Executes Python code in an isolated sandbox environment.
|
||||||
|
|
||||||
Common data-science packages are pre-installed (pandas, numpy, matplotlib,
|
Common data-science packages are pre-installed (pandas, numpy, matplotlib,
|
||||||
scipy, scikit-learn).
|
scipy, scikit-learn).
|
||||||
|
|
||||||
When to use this tool: use execute_code for numerical computation, data
|
When to use this tool: use execute_code for numerical computation, data
|
||||||
analysis, statistics, and any task that benefits from running shell
|
analysis, statistics, and any task that benefits from running Python code.
|
||||||
commands or Python code. Never perform arithmetic manually when this tool
|
Never perform arithmetic manually when this tool is available.
|
||||||
is available.
|
|
||||||
|
|
||||||
Usage notes:
|
Usage notes:
|
||||||
- No outbound network access.
|
- No outbound network access.
|
||||||
- Returns combined stdout/stderr with exit code.
|
- Returns combined stdout/stderr with exit code.
|
||||||
|
- Use print() to produce output.
|
||||||
|
- You can create files, run shell commands via subprocess or os.system(),
|
||||||
|
and use any standard library module.
|
||||||
- Use the optional timeout parameter to override the default timeout.
|
- Use the optional timeout parameter to override the default timeout.
|
||||||
- Chain multiple commands with ';' or '&&'.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SURFSENSE_SAVE_DOCUMENT_TOOL_DESCRIPTION = """Permanently saves a document to the user's knowledge base.
|
SURFSENSE_SAVE_DOCUMENT_TOOL_DESCRIPTION = """Permanently saves a document to the user's knowledge base.
|
||||||
|
|
@ -193,10 +194,10 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
system_prompt = SURFSENSE_FILESYSTEM_SYSTEM_PROMPT
|
system_prompt = SURFSENSE_FILESYSTEM_SYSTEM_PROMPT
|
||||||
if self._sandbox_available:
|
if self._sandbox_available:
|
||||||
system_prompt += (
|
system_prompt += (
|
||||||
"\n- execute_code: run shell commands in an isolated Python sandbox."
|
"\n- execute_code: run Python code in an isolated sandbox."
|
||||||
"\n\n## Code Execution"
|
"\n\n## Code Execution"
|
||||||
"\n\nUse execute_code for numerical computation, data analysis, and"
|
"\n\nUse execute_code whenever a task benefits from running code."
|
||||||
" statistics — never do arithmetic manually."
|
" Never perform arithmetic manually."
|
||||||
"\n\nDocuments here are XML-wrapped markdown, not raw data files."
|
"\n\nDocuments here are XML-wrapped markdown, not raw data files."
|
||||||
" To work with them programmatically, read the document first,"
|
" To work with them programmatically, read the document first,"
|
||||||
" extract the data, write it as a clean file (CSV, JSON, etc.),"
|
" extract the data, write it as a clean file (CSV, JSON, etc.),"
|
||||||
|
|
@ -508,12 +509,12 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
|
|
||||||
def sync_execute_code(
|
def sync_execute_code(
|
||||||
command: Annotated[
|
command: Annotated[
|
||||||
str, "Shell command to execute in the sandbox environment."
|
str, "Python code to execute. Use print() to see output."
|
||||||
],
|
],
|
||||||
runtime: ToolRuntime[None, FilesystemState],
|
runtime: ToolRuntime[None, FilesystemState],
|
||||||
timeout: Annotated[
|
timeout: Annotated[
|
||||||
int | None,
|
int | None,
|
||||||
"Optional timeout in seconds for this command.",
|
"Optional timeout in seconds.",
|
||||||
] = None,
|
] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
|
|
@ -527,12 +528,12 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
|
|
||||||
async def async_execute_code(
|
async def async_execute_code(
|
||||||
command: Annotated[
|
command: Annotated[
|
||||||
str, "Shell command to execute in the sandbox environment."
|
str, "Python code to execute. Use print() to see output."
|
||||||
],
|
],
|
||||||
runtime: ToolRuntime[None, FilesystemState],
|
runtime: ToolRuntime[None, FilesystemState],
|
||||||
timeout: Annotated[
|
timeout: Annotated[
|
||||||
int | None,
|
int | None,
|
||||||
"Optional timeout in seconds for this command.",
|
"Optional timeout in seconds.",
|
||||||
] = None,
|
] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
|
|
@ -549,6 +550,11 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
coroutine=async_execute_code,
|
coroutine=async_execute_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _wrap_as_python(code: str) -> str:
|
||||||
|
"""Wrap Python code in a shell invocation for the sandbox."""
|
||||||
|
return f"python3 << 'PYEOF'\n{code}\nPYEOF"
|
||||||
|
|
||||||
async def _execute_in_sandbox(
|
async def _execute_in_sandbox(
|
||||||
self,
|
self,
|
||||||
command: str,
|
command: str,
|
||||||
|
|
@ -557,6 +563,7 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Core logic: get sandbox, sync files, run command, handle retries."""
|
"""Core logic: get sandbox, sync files, run command, handle retries."""
|
||||||
assert self._thread_id is not None
|
assert self._thread_id is not None
|
||||||
|
command = self._wrap_as_python(command)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return await self._try_sandbox_execute(command, runtime, timeout)
|
return await self._try_sandbox_execute(command, runtime, timeout)
|
||||||
|
|
@ -585,6 +592,12 @@ class SurfSenseFilesystemMiddleware(FilesystemMiddleware):
|
||||||
files = runtime.state.get("files") or {}
|
files = runtime.state.get("files") or {}
|
||||||
await sync_files_to_sandbox(self._thread_id, files, sandbox, is_new)
|
await sync_files_to_sandbox(self._thread_id, files, sandbox, is_new)
|
||||||
result = await sandbox.aexecute(command, timeout=timeout)
|
result = await sandbox.aexecute(command, timeout=timeout)
|
||||||
|
output = (result.output or "").strip()
|
||||||
|
if not output and result.exit_code == 0:
|
||||||
|
return (
|
||||||
|
"[Code executed successfully but produced no output. "
|
||||||
|
"Use print() to display results, then try again.]"
|
||||||
|
)
|
||||||
parts = [result.output]
|
parts = [result.output]
|
||||||
if result.exit_code is not None:
|
if result.exit_code is not None:
|
||||||
status = "succeeded" if result.exit_code == 0 else "failed"
|
status = "succeeded" if result.exit_code == 0 else "failed"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue