Przeglądaj źródła

enh: configurable jupyter execution timeout

Timothy Jaeryang Baek 2 miesięcy temu
rodzic
commit
fe5c4b95d5

+ 4 - 0
backend/open_webui/main.py

@@ -106,6 +106,7 @@ from open_webui.config import (
     CODE_EXECUTION_JUPYTER_AUTH,
     CODE_EXECUTION_JUPYTER_AUTH,
     CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
     CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
+    CODE_EXECUTION_JUPYTER_TIMEOUT,
     ENABLE_CODE_INTERPRETER,
     ENABLE_CODE_INTERPRETER,
     CODE_INTERPRETER_ENGINE,
     CODE_INTERPRETER_ENGINE,
     CODE_INTERPRETER_PROMPT_TEMPLATE,
     CODE_INTERPRETER_PROMPT_TEMPLATE,
@@ -113,6 +114,7 @@ from open_webui.config import (
     CODE_INTERPRETER_JUPYTER_AUTH,
     CODE_INTERPRETER_JUPYTER_AUTH,
     CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
     CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
+    CODE_INTERPRETER_JUPYTER_TIMEOUT,
     # Image
     # Image
     AUTOMATIC1111_API_AUTH,
     AUTOMATIC1111_API_AUTH,
     AUTOMATIC1111_BASE_URL,
     AUTOMATIC1111_BASE_URL,
@@ -644,6 +646,7 @@ app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = CODE_EXECUTION_JUPYTER_AUTH
 app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
 app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
 )
 )
+app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT = CODE_EXECUTION_JUPYTER_TIMEOUT
 
 
 app.state.config.ENABLE_CODE_INTERPRETER = ENABLE_CODE_INTERPRETER
 app.state.config.ENABLE_CODE_INTERPRETER = ENABLE_CODE_INTERPRETER
 app.state.config.CODE_INTERPRETER_ENGINE = CODE_INTERPRETER_ENGINE
 app.state.config.CODE_INTERPRETER_ENGINE = CODE_INTERPRETER_ENGINE
@@ -657,6 +660,7 @@ app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = (
 app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
 app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
 )
 )
+app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT = CODE_INTERPRETER_JUPYTER_TIMEOUT
 
 
 ########################################
 ########################################
 #
 #

+ 12 - 0
backend/open_webui/routers/configs.py

@@ -75,6 +75,7 @@ class CodeInterpreterConfigForm(BaseModel):
     CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
     CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
     CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
     CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
     CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
+    CODE_EXECUTION_JUPYTER_TIMEOUT: Optional[int]
     ENABLE_CODE_INTERPRETER: bool
     ENABLE_CODE_INTERPRETER: bool
     CODE_INTERPRETER_ENGINE: str
     CODE_INTERPRETER_ENGINE: str
     CODE_INTERPRETER_PROMPT_TEMPLATE: Optional[str]
     CODE_INTERPRETER_PROMPT_TEMPLATE: Optional[str]
@@ -82,6 +83,7 @@ class CodeInterpreterConfigForm(BaseModel):
     CODE_INTERPRETER_JUPYTER_AUTH: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
+    CODE_INTERPRETER_JUPYTER_TIMEOUT: Optional[int]
 
 
 
 
 @router.get("/code_execution", response_model=CodeInterpreterConfigForm)
 @router.get("/code_execution", response_model=CodeInterpreterConfigForm)
@@ -92,6 +94,7 @@ async def get_code_execution_config(request: Request, user=Depends(get_admin_use
         "CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
         "CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
         "CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
         "CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
         "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
         "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
+        "CODE_EXECUTION_JUPYTER_TIMEOUT": request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
         "ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
         "ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
         "CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
         "CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
         "CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
         "CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
@@ -99,6 +102,7 @@ async def get_code_execution_config(request: Request, user=Depends(get_admin_use
         "CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
         "CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
         "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
         "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
         "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
         "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
+        "CODE_INTERPRETER_JUPYTER_TIMEOUT": request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
     }
     }
 
 
 
 
@@ -120,6 +124,9 @@ async def set_code_execution_config(
     request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
     request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
         form_data.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
         form_data.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
     )
     )
+    request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT = (
+        form_data.CODE_EXECUTION_JUPYTER_TIMEOUT
+    )
 
 
     request.app.state.config.ENABLE_CODE_INTERPRETER = form_data.ENABLE_CODE_INTERPRETER
     request.app.state.config.ENABLE_CODE_INTERPRETER = form_data.ENABLE_CODE_INTERPRETER
     request.app.state.config.CODE_INTERPRETER_ENGINE = form_data.CODE_INTERPRETER_ENGINE
     request.app.state.config.CODE_INTERPRETER_ENGINE = form_data.CODE_INTERPRETER_ENGINE
@@ -141,6 +148,9 @@ async def set_code_execution_config(
     request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
     request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
         form_data.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
         form_data.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
     )
     )
+    request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT = (
+        form_data.CODE_INTERPRETER_JUPYTER_TIMEOUT
+    )
 
 
     return {
     return {
         "CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
         "CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
@@ -148,6 +158,7 @@ async def set_code_execution_config(
         "CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
         "CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
         "CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
         "CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
         "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
         "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
+        "CODE_EXECUTION_JUPYTER_TIMEOUT": request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
         "ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
         "ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
         "CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
         "CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
         "CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
         "CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
@@ -155,6 +166,7 @@ async def set_code_execution_config(
         "CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
         "CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
         "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
         "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
         "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
         "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
+        "CODE_INTERPRETER_JUPYTER_TIMEOUT": request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
     }
     }
 
 
 
 

+ 1 - 0
backend/open_webui/routers/utils.py

@@ -56,6 +56,7 @@ async def execute_code(
                 if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "password"
                 if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "password"
                 else None
                 else None
             ),
             ),
+            request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
         )
         )
 
 
         return output
         return output

+ 1 - 0
backend/open_webui/utils/middleware.py

@@ -1761,6 +1761,7 @@ async def process_chat_response(
                                             == "password"
                                             == "password"
                                             else None
                                             else None
                                         ),
                                         ),
+                                        request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
                                     )
                                     )
                                 else:
                                 else:
                                     output = {
                                     output = {

+ 107 - 67
src/lib/components/admin/Settings/CodeExecution.svelte

@@ -91,45 +91,65 @@
 							</div>
 							</div>
 						</div>
 						</div>
 
 
-						<div class=" flex gap-2 w-full items-center justify-between">
-							<div class="text-xs font-medium">
-								{$i18n.t('Jupyter Auth')}
-							</div>
+						<div class="mb-2.5 flex flex-col gap-1.5 w-full">
+							<div class=" flex gap-2 w-full items-center justify-between">
+								<div class="text-xs font-medium">
+									{$i18n.t('Jupyter Auth')}
+								</div>
 
 
-							<div>
-								<select
-									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
-									bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
-									placeholder={$i18n.t('Select an auth method')}
-								>
-									<option selected value="">{$i18n.t('None')}</option>
-									<option value="token">{$i18n.t('Token')}</option>
-									<option value="password">{$i18n.t('Password')}</option>
-								</select>
+								<div>
+									<select
+										class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
+										bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
+										placeholder={$i18n.t('Select an auth method')}
+									>
+										<option selected value="">{$i18n.t('None')}</option>
+										<option value="token">{$i18n.t('Token')}</option>
+										<option value="password">{$i18n.t('Password')}</option>
+									</select>
+								</div>
 							</div>
 							</div>
-						</div>
 
 
-						{#if config.CODE_EXECUTION_JUPYTER_AUTH}
-							<div class="flex w-full gap-2">
-								<div class="flex-1">
-									{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
-										<SensitiveInput
-											type="text"
-											placeholder={$i18n.t('Enter Jupyter Password')}
-											bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
-											autocomplete="off"
-										/>
-									{:else}
-										<SensitiveInput
-											type="text"
-											placeholder={$i18n.t('Enter Jupyter Token')}
-											bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
-											autocomplete="off"
-										/>
-									{/if}
+							{#if config.CODE_EXECUTION_JUPYTER_AUTH}
+								<div class="flex w-full gap-2">
+									<div class="flex-1">
+										{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
+											<SensitiveInput
+												type="text"
+												placeholder={$i18n.t('Enter Jupyter Password')}
+												bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
+												autocomplete="off"
+											/>
+										{:else}
+											<SensitiveInput
+												type="text"
+												placeholder={$i18n.t('Enter Jupyter Token')}
+												bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
+												autocomplete="off"
+											/>
+										{/if}
+									</div>
 								</div>
 								</div>
+							{/if}
+						</div>
+
+						<div class="flex gap-2 w-full items-center justify-between">
+							<div class="text-xs font-medium">
+								{$i18n.t('Code Execution Timeout')}
 							</div>
 							</div>
-						{/if}
+
+							<div class="">
+								<Tooltip content={$i18n.t('Enter timeout in seconds')}>
+									<input
+										class="dark:bg-gray-900 w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+										type="number"
+										bind:value={config.CODE_EXECUTION_JUPYTER_TIMEOUT}
+										placeholder={$i18n.t('e.g. 60')}
+										autocomplete="off"
+									/>
+								</Tooltip>
+							</div>
+						</div>
 					{/if}
 					{/if}
 				</div>
 				</div>
 
 
@@ -197,45 +217,65 @@
 								</div>
 								</div>
 							</div>
 							</div>
 
 
+							<div class="mb-2.5 flex flex-col gap-1.5 w-full">
+								<div class="flex gap-2 w-full items-center justify-between">
+									<div class="text-xs font-medium">
+										{$i18n.t('Jupyter Auth')}
+									</div>
+
+									<div>
+										<select
+											class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
+											bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
+											placeholder={$i18n.t('Select an auth method')}
+										>
+											<option selected value="">{$i18n.t('None')}</option>
+											<option value="token">{$i18n.t('Token')}</option>
+											<option value="password">{$i18n.t('Password')}</option>
+										</select>
+									</div>
+								</div>
+
+								{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
+									<div class="flex w-full gap-2">
+										<div class="flex-1">
+											{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
+												<SensitiveInput
+													type="text"
+													placeholder={$i18n.t('Enter Jupyter Password')}
+													bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
+													autocomplete="off"
+												/>
+											{:else}
+												<SensitiveInput
+													type="text"
+													placeholder={$i18n.t('Enter Jupyter Token')}
+													bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
+													autocomplete="off"
+												/>
+											{/if}
+										</div>
+									</div>
+								{/if}
+							</div>
+
 							<div class="flex gap-2 w-full items-center justify-between">
 							<div class="flex gap-2 w-full items-center justify-between">
 								<div class="text-xs font-medium">
 								<div class="text-xs font-medium">
-									{$i18n.t('Jupyter Auth')}
+									{$i18n.t('Code Execution Timeout')}
 								</div>
 								</div>
 
 
-								<div>
-									<select
-										class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
-										bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
-										placeholder={$i18n.t('Select an auth method')}
-									>
-										<option selected value="">{$i18n.t('None')}</option>
-										<option value="token">{$i18n.t('Token')}</option>
-										<option value="password">{$i18n.t('Password')}</option>
-									</select>
+								<div class="">
+									<Tooltip content={$i18n.t('Enter timeout in seconds')}>
+										<input
+											class="dark:bg-gray-900 w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+											type="number"
+											bind:value={config.CODE_INTERPRETER_JUPYTER_TIMEOUT}
+											placeholder={$i18n.t('e.g. 60')}
+											autocomplete="off"
+										/>
+									</Tooltip>
 								</div>
 								</div>
 							</div>
 							</div>
-
-							{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
-								<div class="flex w-full gap-2">
-									<div class="flex-1">
-										{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
-											<SensitiveInput
-												type="text"
-												placeholder={$i18n.t('Enter Jupyter Password')}
-												bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
-												autocomplete="off"
-											/>
-										{:else}
-											<SensitiveInput
-												type="text"
-												placeholder={$i18n.t('Enter Jupyter Token')}
-												bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
-												autocomplete="off"
-											/>
-										{/if}
-									</div>
-								</div>
-							{/if}
 						{/if}
 						{/if}
 
 
 						<hr class="border-gray-100 dark:border-gray-850 my-2" />
 						<hr class="border-gray-100 dark:border-gray-850 my-2" />

+ 8 - 6
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -123,6 +123,12 @@
 	};
 	};
 
 
 	const executePython = async (code) => {
 	const executePython = async (code) => {
+		result = null;
+		stdout = null;
+		stderr = null;
+
+		executing = true;
+
 		if ($config?.code?.engine === 'jupyter') {
 		if ($config?.code?.engine === 'jupyter') {
 			const output = await executeCode(localStorage.token, code).catch((error) => {
 			const output = await executeCode(localStorage.token, code).catch((error) => {
 				toast.error(`${error}`);
 				toast.error(`${error}`);
@@ -190,18 +196,14 @@
 
 
 				output['stderr'] && (stderr = output['stderr']);
 				output['stderr'] && (stderr = output['stderr']);
 			}
 			}
+
+			executing = false;
 		} else {
 		} else {
 			executePythonAsWorker(code);
 			executePythonAsWorker(code);
 		}
 		}
 	};
 	};
 
 
 	const executePythonAsWorker = async (code) => {
 	const executePythonAsWorker = async (code) => {
-		result = null;
-		stdout = null;
-		stderr = null;
-
-		executing = true;
-
 		let packages = [
 		let packages = [
 			code.includes('requests') ? 'requests' : null,
 			code.includes('requests') ? 'requests' : null,
 			code.includes('bs4') ? 'beautifulsoup4' : null,
 			code.includes('bs4') ? 'beautifulsoup4' : null,