diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 2fb967edd6..339c03a485 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -3125,7 +3125,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): if name not in tools_dict: tools_dict[name] = tool_dict - # Only advertise user-shell tools when the originating browser has a connected shell. + # Connections can keep shell tools advertised across browser disconnects for prompt caching. shell_tools = { name: tool for name, tool in tools_dict.items() @@ -3133,7 +3133,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): and (tool.get('type') == 'terminal' or tool.get('server', {}).get('is_terminal') is True) } selected = { - name + name: tool for name, tool in shell_tools.items() if terminal_id and ( @@ -3141,9 +3141,21 @@ async def process_chat_payload(request, form_data, user, metadata, model): or (tool.get('direct') and tool.get('server', {}).get('url') == terminal_id) ) } - connected = False + connected = ( + any( + tool.get('user_shell_tools') == 'always' + or ( + tool.get('direct') + and (tool.get('server', {}).get('config') or {}).get('user_shell_tools') == 'always' + ) + for tool in selected.values() + ) + and not metadata.get('automation_id') + and not metadata.get('internal') + ) if ( selected + and not connected and event_caller and metadata.get('session_id') and metadata.get('chat_id') diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 8f22cfc3a0..c38e7ea1cd 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -1507,10 +1507,15 @@ async def get_terminal_tools( headers[TERMINAL_CONTEXT_HEADER] = context_id # Fetch live with the user's credentials so prompt changes apply without a restart - terminal_cwd, system_prompt = await asyncio.gather( - get_terminal_cwd(server_data['url'], headers, cookies), - get_terminal_system_prompt(server_data['url'], headers, cookies), - ) + connection_config = connection.get('config') or {} + terminal_cwd = None + if connection_config.get('working_directory_context', True): + terminal_cwd, system_prompt = await asyncio.gather( + get_terminal_cwd(server_data['url'], headers, cookies), + get_terminal_system_prompt(server_data['url'], headers, cookies), + ) + else: + system_prompt = await get_terminal_system_prompt(server_data['url'], headers, cookies) if not system_prompt: system_prompt = server_data.get('system_prompt') @@ -1548,6 +1553,7 @@ async def get_terminal_tools( 'callable': callable, 'spec': tool_spec, 'type': 'terminal', + 'user_shell_tools': connection_config.get('user_shell_tools', 'auto'), } return tools_dict, system_prompt diff --git a/src/lib/components/AddTerminalServerModal.svelte b/src/lib/components/AddTerminalServerModal.svelte index eba77c8f97..19dae03917 100644 --- a/src/lib/components/AddTerminalServerModal.svelte +++ b/src/lib/components/AddTerminalServerModal.svelte @@ -41,6 +41,8 @@ let path = '/openapi.json'; let enabled = false; let chatUploads: 'default' | 'filesystem' = 'default'; + let workingDirectoryContext = true; + let userShellTools: 'auto' | 'always' = 'auto'; let chatContextMode: 'default' | 'chat_id' | 'off' = 'default'; let automationContextMode: 'default' | 'automation_id' | 'off' = 'default'; let showAdvanced = false; @@ -78,6 +80,8 @@ const init = () => { forwardCookies = connection?.forward_cookies ?? false; + workingDirectoryContext = connection?.config?.working_directory_context ?? true; + userShellTools = connection?.config?.user_shell_tools === 'always' ? 'always' : 'auto'; if (connection) { id = connection?.id ?? ''; url = connection.url; @@ -402,6 +406,8 @@ else delete connectionConfig.contexts; if (chatUploads === 'filesystem') connectionConfig.chat_uploads = 'filesystem'; else delete connectionConfig.chat_uploads; + if (!direct) connectionConfig.working_directory_context = workingDirectoryContext; + connectionConfig.user_shell_tools = userShellTools; const result = { ...(!direct && id.trim() ? { id: id.trim() } : {}), @@ -898,7 +904,55 @@ {#if !direct}
+ {#if userShellTools === 'auto'} + {$i18n.t('Include tools only while your shell is connected.')} + {:else} + {$i18n.t('Keep tools listed even while your shell is closed.')} + {/if} +
+@@ -917,7 +971,7 @@