From b7105e71d3b640c102c68142e17508417704043f Mon Sep 17 00:00:00 2001 From: marko-kraemer Date: Mon, 21 Apr 2025 13:41:01 +0100 Subject: [PATCH] hide attachments, non-blocking shell exec --- backend/agent/prompt.py | 8 +++ backend/agent/tools/sb_shell_tool.py | 45 +++++++++++++--- .../src/app/(dashboard)/dashboard/page.tsx | 1 + frontend/src/components/thread/chat-input.tsx | 52 ++++++++++--------- .../tool-views/FileOperationToolView.tsx | 8 ++- 5 files changed, 77 insertions(+), 37 deletions(-) diff --git a/backend/agent/prompt.py b/backend/agent/prompt.py index 37ed83c2..1826d81b 100644 --- a/backend/agent/prompt.py +++ b/backend/agent/prompt.py @@ -108,6 +108,14 @@ You have the ability to execute operations using both Python and CLI tools: - Always clean up sessions after use - Avoid commands requiring confirmation; actively use -y or -f flags for automatic confirmation - Avoid commands with excessive output; save to files when necessary +- **IMPORTANT**: Shell commands are blocking by default - they will not return control until the command completes, which can cause timeouts with long-running operations +- For non-blocking, long-running commands, use these simple approaches: + 1. Run a command in the background using `&`: `command &` + 2. Make a process immune to hangups: `nohup command > output.log 2>&1 &` + 3. Start a background process and get its PID: `command & echo $!` + 4. Check if a process is still running: `ps -p PID_NUMBER` + 5. View output of a background process: `tail -f output.log` + 6. Kill a background process: `kill PID_NUMBER` or `pkill PROCESS_NAME` - Chain multiple commands with operators to minimize interruptions and improve efficiency: 1. Use && for sequential execution: `command1 && command2 && command3` 2. Use || for fallback execution: `command1 || command2` diff --git a/backend/agent/tools/sb_shell_tool.py b/backend/agent/tools/sb_shell_tool.py index a0a5a94c..ccc11982 100644 --- a/backend/agent/tools/sb_shell_tool.py +++ b/backend/agent/tools/sb_shell_tool.py @@ -36,7 +36,7 @@ class SandboxShellTool(SandboxToolsBase): "type": "function", "function": { "name": "execute_command", - "description": "Execute a shell command in the workspace directory. Uses sessions to maintain state between commands. This tool is essential for running CLI tools, installing packages, and managing system operations. Always verify command outputs before using the data. Commands can be chained using && for sequential execution, || for fallback execution, and | for piping output.", + "description": "Execute a shell command in the workspace directory. IMPORTANT: By default, commands are blocking and will wait for completion before returning. For long-running operations, use background execution techniques (& operator, nohup) to prevent timeouts. Uses sessions to maintain state between commands. This tool is essential for running CLI tools, installing packages, and managing system operations. Always verify command outputs before using the data. Commands can be chained using && for sequential execution, || for fallback execution, and | for piping output.", "parameters": { "type": "object", "properties": { @@ -55,7 +55,7 @@ class SandboxShellTool(SandboxToolsBase): }, "timeout": { "type": "integer", - "description": "Optional timeout in seconds. Increase for long-running commands. Defaults to 60.", + "description": "Optional timeout in seconds. Increase for long-running commands. Defaults to 60. For commands that might exceed this timeout, use background execution with & operator instead.", "default": 60 } }, @@ -72,30 +72,59 @@ class SandboxShellTool(SandboxToolsBase): {"param_name": "timeout", "node_type": "attribute", "path": ".", "required": False} ], example=''' - + + + ls -l - + pdftotext document.pdf -layout - + pdftotext input.pdf -layout > output.txt - + find . -type f -name "*.txt" | sort && grep -r "pattern" . | awk '{print $1}' | sort | uniq -c - + pdftotext input.pdf -layout 2>&1 || echo "Error processing PDF" && ls -la output.txt + + + + + + python scraper.py --large-dataset > scraper_output.log 2>&1 & + + + + + nohup python processor.py --heavy-computation > processor.log 2>&1 & + + + + + python long_task.py & echo $! > task.pid + + + + + ps -p $(cat task.pid) + + + + + kill $(cat task.pid) + ''' ) async def execute_command( @@ -122,7 +151,7 @@ class SandboxShellTool(SandboxToolsBase): from sandbox.sandbox import SessionExecuteRequest req = SessionExecuteRequest( command=command, - var_async=False, + var_async=False, # This makes the command blocking by default cwd=cwd # Still set the working directory for reference ) diff --git a/frontend/src/app/(dashboard)/dashboard/page.tsx b/frontend/src/app/(dashboard)/dashboard/page.tsx index 29f6d5de..2bfc6b8e 100644 --- a/frontend/src/app/(dashboard)/dashboard/page.tsx +++ b/frontend/src/app/(dashboard)/dashboard/page.tsx @@ -96,6 +96,7 @@ function DashboardContent() { placeholder="Describe what you need help with..." value={inputValue} onChange={setInputValue} + hideAttachments={true} /> diff --git a/frontend/src/components/thread/chat-input.tsx b/frontend/src/components/thread/chat-input.tsx index 1e34f45f..d3a0f3e9 100644 --- a/frontend/src/components/thread/chat-input.tsx +++ b/frontend/src/components/thread/chat-input.tsx @@ -42,6 +42,7 @@ interface ChatInputProps { onChange?: (value: string) => void; onFileBrowse?: () => void; sandboxId?: string; + hideAttachments?: boolean; } interface UploadedFile { @@ -61,7 +62,8 @@ export function ChatInput({ value: controlledValue, onChange: controlledOnChange, onFileBrowse, - sandboxId + sandboxId, + hideAttachments = false }: ChatInputProps) { const isControlled = controlledValue !== undefined && controlledOnChange !== undefined; @@ -398,29 +400,31 @@ export function ChatInput({ )} */} - - - - - - -

Attach files

-
-
-
+ {!hideAttachments && ( + + + + + + +

Attach files

+
+
+
+ )} - - {fileContent} - +
+
)}