Explorar o código

enh: code execution settings

Timothy Jaeryang Baek hai 2 meses
pai
achega
2f75eef499

+ 48 - 4
backend/open_webui/config.py

@@ -1377,6 +1377,39 @@ Responses from models: {{responses}}"""
 # Code Interpreter
 # Code Interpreter
 ####################################
 ####################################
 
 
+
+CODE_EXECUTION_ENGINE = PersistentConfig(
+    "CODE_EXECUTION_ENGINE",
+    "code_execution.engine",
+    os.environ.get("CODE_EXECUTION_ENGINE", "pyodide"),
+)
+
+CODE_EXECUTION_JUPYTER_URL = PersistentConfig(
+    "CODE_EXECUTION_JUPYTER_URL",
+    "code_execution.jupyter.url",
+    os.environ.get("CODE_EXECUTION_JUPYTER_URL", ""),
+)
+
+CODE_EXECUTION_JUPYTER_AUTH = PersistentConfig(
+    "CODE_EXECUTION_JUPYTER_AUTH",
+    "code_execution.jupyter.auth",
+    os.environ.get("CODE_EXECUTION_JUPYTER_AUTH", ""),
+)
+
+CODE_EXECUTION_JUPYTER_AUTH_TOKEN = PersistentConfig(
+    "CODE_EXECUTION_JUPYTER_AUTH_TOKEN",
+    "code_execution.jupyter.auth_token",
+    os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_TOKEN", ""),
+)
+
+
+CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = PersistentConfig(
+    "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD",
+    "code_execution.jupyter.auth_password",
+    os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", ""),
+)
+
+
 ENABLE_CODE_INTERPRETER = PersistentConfig(
 ENABLE_CODE_INTERPRETER = PersistentConfig(
     "ENABLE_CODE_INTERPRETER",
     "ENABLE_CODE_INTERPRETER",
     "code_interpreter.enable",
     "code_interpreter.enable",
@@ -1398,26 +1431,37 @@ CODE_INTERPRETER_PROMPT_TEMPLATE = PersistentConfig(
 CODE_INTERPRETER_JUPYTER_URL = PersistentConfig(
 CODE_INTERPRETER_JUPYTER_URL = PersistentConfig(
     "CODE_INTERPRETER_JUPYTER_URL",
     "CODE_INTERPRETER_JUPYTER_URL",
     "code_interpreter.jupyter.url",
     "code_interpreter.jupyter.url",
-    os.environ.get("CODE_INTERPRETER_JUPYTER_URL", ""),
+    os.environ.get(
+        "CODE_INTERPRETER_JUPYTER_URL", os.environ.get("CODE_EXECUTION_JUPYTER_URL", "")
+    ),
 )
 )
 
 
 CODE_INTERPRETER_JUPYTER_AUTH = PersistentConfig(
 CODE_INTERPRETER_JUPYTER_AUTH = PersistentConfig(
     "CODE_INTERPRETER_JUPYTER_AUTH",
     "CODE_INTERPRETER_JUPYTER_AUTH",
     "code_interpreter.jupyter.auth",
     "code_interpreter.jupyter.auth",
-    os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH", ""),
+    os.environ.get(
+        "CODE_INTERPRETER_JUPYTER_AUTH",
+        os.environ.get("CODE_EXECUTION_JUPYTER_AUTH", ""),
+    ),
 )
 )
 
 
 CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = PersistentConfig(
 CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = PersistentConfig(
     "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
     "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
     "code_interpreter.jupyter.auth_token",
     "code_interpreter.jupyter.auth_token",
-    os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_TOKEN", ""),
+    os.environ.get(
+        "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
+        os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_TOKEN", ""),
+    ),
 )
 )
 
 
 
 
 CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = PersistentConfig(
 CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = PersistentConfig(
     "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
     "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
     "code_interpreter.jupyter.auth_password",
     "code_interpreter.jupyter.auth_password",
-    os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD", ""),
+    os.environ.get(
+        "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
+        os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", ""),
+    ),
 )
 )
 
 
 
 

+ 18 - 2
backend/open_webui/main.py

@@ -100,7 +100,12 @@ from open_webui.config import (
     OPENAI_API_CONFIGS,
     OPENAI_API_CONFIGS,
     # Direct Connections
     # Direct Connections
     ENABLE_DIRECT_CONNECTIONS,
     ENABLE_DIRECT_CONNECTIONS,
-    # Code Interpreter
+    # Code Execution
+    CODE_EXECUTION_ENGINE,
+    CODE_EXECUTION_JUPYTER_URL,
+    CODE_EXECUTION_JUPYTER_AUTH,
+    CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
+    CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
     ENABLE_CODE_INTERPRETER,
     ENABLE_CODE_INTERPRETER,
     CODE_INTERPRETER_ENGINE,
     CODE_INTERPRETER_ENGINE,
     CODE_INTERPRETER_PROMPT_TEMPLATE,
     CODE_INTERPRETER_PROMPT_TEMPLATE,
@@ -613,10 +618,18 @@ app.state.EMBEDDING_FUNCTION = get_embedding_function(
 
 
 ########################################
 ########################################
 #
 #
-# CODE INTERPRETER
+# CODE EXECUTION
 #
 #
 ########################################
 ########################################
 
 
+app.state.config.CODE_EXECUTION_ENGINE = CODE_EXECUTION_ENGINE
+app.state.config.CODE_EXECUTION_JUPYTER_URL = CODE_EXECUTION_JUPYTER_URL
+app.state.config.CODE_EXECUTION_JUPYTER_AUTH = CODE_EXECUTION_JUPYTER_AUTH
+app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = CODE_EXECUTION_JUPYTER_AUTH_TOKEN
+app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
+    CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
+)
+
 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
 app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = CODE_INTERPRETER_PROMPT_TEMPLATE
 app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = CODE_INTERPRETER_PROMPT_TEMPLATE
@@ -1120,6 +1133,9 @@ async def get_app_config(request: Request):
             {
             {
                 "default_models": app.state.config.DEFAULT_MODELS,
                 "default_models": app.state.config.DEFAULT_MODELS,
                 "default_prompt_suggestions": app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
                 "default_prompt_suggestions": app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
+                "code": {
+                    "engine": app.state.config.CODE_EXECUTION_ENGINE,
+                },
                 "audio": {
                 "audio": {
                     "tts": {
                     "tts": {
                         "engine": app.state.config.TTS_ENGINE,
                         "engine": app.state.config.TTS_ENGINE,

+ 34 - 4
backend/open_webui/routers/configs.py

@@ -70,6 +70,11 @@ async def set_direct_connections_config(
 # CodeInterpreterConfig
 # CodeInterpreterConfig
 ############################
 ############################
 class CodeInterpreterConfigForm(BaseModel):
 class CodeInterpreterConfigForm(BaseModel):
+    CODE_EXECUTION_ENGINE: str
+    CODE_EXECUTION_JUPYTER_URL: Optional[str]
+    CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
+    CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
+    CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
     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]
@@ -79,9 +84,14 @@ class CodeInterpreterConfigForm(BaseModel):
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
     CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
 
 
 
 
-@router.get("/code_interpreter", response_model=CodeInterpreterConfigForm)
-async def get_code_interpreter_config(request: Request, user=Depends(get_admin_user)):
+@router.get("/code_execution", response_model=CodeInterpreterConfigForm)
+async def get_code_execution_config(request: Request, user=Depends(get_admin_user)):
     return {
     return {
+        "CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
+        "CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
+        "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_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
         "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,
@@ -92,10 +102,25 @@ async def get_code_interpreter_config(request: Request, user=Depends(get_admin_u
     }
     }
 
 
 
 
-@router.post("/code_interpreter", response_model=CodeInterpreterConfigForm)
-async def set_code_interpreter_config(
+@router.post("/code_execution", response_model=CodeInterpreterConfigForm)
+async def set_code_execution_config(
     request: Request, form_data: CodeInterpreterConfigForm, user=Depends(get_admin_user)
     request: Request, form_data: CodeInterpreterConfigForm, user=Depends(get_admin_user)
 ):
 ):
+
+    request.app.state.config.CODE_EXECUTION_ENGINE = form_data.CODE_EXECUTION_ENGINE
+    request.app.state.config.CODE_EXECUTION_JUPYTER_URL = (
+        form_data.CODE_EXECUTION_JUPYTER_URL
+    )
+    request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH = (
+        form_data.CODE_EXECUTION_JUPYTER_AUTH
+    )
+    request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = (
+        form_data.CODE_EXECUTION_JUPYTER_AUTH_TOKEN
+    )
+    request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
+        form_data.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
+    )
+
     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
     request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = (
     request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = (
@@ -118,6 +143,11 @@ async def set_code_interpreter_config(
     )
     )
 
 
     return {
     return {
+        "CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
+        "CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
+        "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_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
         "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,

+ 41 - 11
backend/open_webui/routers/utils.py

@@ -4,45 +4,75 @@ import markdown
 from open_webui.models.chats import ChatTitleMessagesForm
 from open_webui.models.chats import ChatTitleMessagesForm
 from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
 from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
 from open_webui.constants import ERROR_MESSAGES
 from open_webui.constants import ERROR_MESSAGES
-from fastapi import APIRouter, Depends, HTTPException, Response, status
+from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
 from pydantic import BaseModel
 from pydantic import BaseModel
 from starlette.responses import FileResponse
 from starlette.responses import FileResponse
+
+
 from open_webui.utils.misc import get_gravatar_url
 from open_webui.utils.misc import get_gravatar_url
 from open_webui.utils.pdf_generator import PDFGenerator
 from open_webui.utils.pdf_generator import PDFGenerator
-from open_webui.utils.auth import get_admin_user
+from open_webui.utils.auth import get_admin_user, get_verified_user
+from open_webui.utils.code_interpreter import execute_code_jupyter
+
 
 
 router = APIRouter()
 router = APIRouter()
 
 
 
 
 @router.get("/gravatar")
 @router.get("/gravatar")
-async def get_gravatar(
-    email: str,
-):
+async def get_gravatar(email: str, user=Depends(get_verified_user)):
     return get_gravatar_url(email)
     return get_gravatar_url(email)
 
 
 
 
-class CodeFormatRequest(BaseModel):
+class CodeForm(BaseModel):
     code: str
     code: str
 
 
 
 
 @router.post("/code/format")
 @router.post("/code/format")
-async def format_code(request: CodeFormatRequest):
+async def format_code(form_data: CodeForm, user=Depends(get_verified_user)):
     try:
     try:
-        formatted_code = black.format_str(request.code, mode=black.Mode())
+        formatted_code = black.format_str(form_data.code, mode=black.Mode())
         return {"code": formatted_code}
         return {"code": formatted_code}
     except black.NothingChanged:
     except black.NothingChanged:
-        return {"code": request.code}
+        return {"code": form_data.code}
     except Exception as e:
     except Exception as e:
         raise HTTPException(status_code=400, detail=str(e))
         raise HTTPException(status_code=400, detail=str(e))
 
 
 
 
+@router.post("/code/execute")
+async def execute_code(
+    request: Request, form_data: CodeForm, user=Depends(get_verified_user)
+):
+    if request.app.state.config.CODE_EXECUTION_ENGINE == "jupyter":
+        output = await execute_code_jupyter(
+            request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
+            form_data.code,
+            (
+                request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN
+                if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "token"
+                else None
+            ),
+            (
+                request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
+                if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "password"
+                else None
+            ),
+        )
+
+        return output
+    else:
+        raise HTTPException(
+            status_code=400,
+            detail="Code execution engine not supported",
+        )
+
+
 class MarkdownForm(BaseModel):
 class MarkdownForm(BaseModel):
     md: str
     md: str
 
 
 
 
 @router.post("/markdown")
 @router.post("/markdown")
 async def get_html_from_markdown(
 async def get_html_from_markdown(
-    form_data: MarkdownForm,
+    form_data: MarkdownForm, user=Depends(get_verified_user)
 ):
 ):
     return {"html": markdown.markdown(form_data.md)}
     return {"html": markdown.markdown(form_data.md)}
 
 
@@ -54,7 +84,7 @@ class ChatForm(BaseModel):
 
 
 @router.post("/pdf")
 @router.post("/pdf")
 async def download_chat_as_pdf(
 async def download_chat_as_pdf(
-    form_data: ChatTitleMessagesForm,
+    form_data: ChatTitleMessagesForm, user=Depends(get_verified_user)
 ):
 ):
     try:
     try:
         pdf_bytes = PDFGenerator(form_data).generate_chat_pdf()
         pdf_bytes = PDFGenerator(form_data).generate_chat_pdf()

+ 4 - 4
src/lib/apis/configs/index.ts

@@ -115,10 +115,10 @@ export const setDirectConnectionsConfig = async (token: string, config: object)
 	return res;
 	return res;
 };
 };
 
 
-export const getCodeInterpreterConfig = async (token: string) => {
+export const getCodeExecutionConfig = async (token: string) => {
 	let error = null;
 	let error = null;
 
 
-	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_execution`, {
 		method: 'GET',
 		method: 'GET',
 		headers: {
 		headers: {
 			'Content-Type': 'application/json',
 			'Content-Type': 'application/json',
@@ -142,10 +142,10 @@ export const getCodeInterpreterConfig = async (token: string) => {
 	return res;
 	return res;
 };
 };
 
 
-export const setCodeInterpreterConfig = async (token: string, config: object) => {
+export const setCodeExecutionConfig = async (token: string, config: object) => {
 	let error = null;
 	let error = null;
 
 
-	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_execution`, {
 		method: 'POST',
 		method: 'POST',
 		headers: {
 		headers: {
 			'Content-Type': 'application/json',
 			'Content-Type': 'application/json',

+ 46 - 8
src/lib/apis/utils/index.ts

@@ -1,12 +1,13 @@
 import { WEBUI_API_BASE_URL } from '$lib/constants';
 import { WEBUI_API_BASE_URL } from '$lib/constants';
 
 
-export const getGravatarUrl = async (email: string) => {
+export const getGravatarUrl = async (token: string, email: string) => {
 	let error = null;
 	let error = null;
 
 
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/gravatar?email=${email}`, {
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/gravatar?email=${email}`, {
 		method: 'GET',
 		method: 'GET',
 		headers: {
 		headers: {
-			'Content-Type': 'application/json'
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
 		}
 		}
 	})
 	})
 		.then(async (res) => {
 		.then(async (res) => {
@@ -22,13 +23,48 @@ export const getGravatarUrl = async (email: string) => {
 	return res;
 	return res;
 };
 };
 
 
-export const formatPythonCode = async (code: string) => {
+export const executeCode = async (token: string, code: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/code/execute`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			code: code
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+
+			error = err;
+			if (err.detail) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const formatPythonCode = async (token: string, code: string) => {
 	let error = null;
 	let error = null;
 
 
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/code/format`, {
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/code/format`, {
 		method: 'POST',
 		method: 'POST',
 		headers: {
 		headers: {
-			'Content-Type': 'application/json'
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
 		},
 		},
 		body: JSON.stringify({
 		body: JSON.stringify({
 			code: code
 			code: code
@@ -55,13 +91,14 @@ export const formatPythonCode = async (code: string) => {
 	return res;
 	return res;
 };
 };
 
 
-export const downloadChatAsPDF = async (title: string, messages: object[]) => {
+export const downloadChatAsPDF = async (token: string, title: string, messages: object[]) => {
 	let error = null;
 	let error = null;
 
 
 	const blob = await fetch(`${WEBUI_API_BASE_URL}/utils/pdf`, {
 	const blob = await fetch(`${WEBUI_API_BASE_URL}/utils/pdf`, {
 		method: 'POST',
 		method: 'POST',
 		headers: {
 		headers: {
-			'Content-Type': 'application/json'
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
 		},
 		},
 		body: JSON.stringify({
 		body: JSON.stringify({
 			title: title,
 			title: title,
@@ -81,13 +118,14 @@ export const downloadChatAsPDF = async (title: string, messages: object[]) => {
 	return blob;
 	return blob;
 };
 };
 
 
-export const getHTMLFromMarkdown = async (md: string) => {
+export const getHTMLFromMarkdown = async (token: string, md: string) => {
 	let error = null;
 	let error = null;
 
 
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/markdown`, {
 	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/markdown`, {
 		method: 'POST',
 		method: 'POST',
 		headers: {
 		headers: {
-			'Content-Type': 'application/json'
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
 		},
 		},
 		body: JSON.stringify({
 		body: JSON.stringify({
 			md: md
 			md: md

+ 6 - 6
src/lib/components/admin/Settings.svelte

@@ -19,7 +19,7 @@
 	import ChartBar from '../icons/ChartBar.svelte';
 	import ChartBar from '../icons/ChartBar.svelte';
 	import DocumentChartBar from '../icons/DocumentChartBar.svelte';
 	import DocumentChartBar from '../icons/DocumentChartBar.svelte';
 	import Evaluations from './Settings/Evaluations.svelte';
 	import Evaluations from './Settings/Evaluations.svelte';
-	import CodeInterpreter from './Settings/CodeInterpreter.svelte';
+	import CodeExecution from './Settings/CodeExecution.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -191,11 +191,11 @@
 
 
 		<button
 		<button
 			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
-			'code-interpreter'
+			'code-execution'
 				? ''
 				? ''
 				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
 				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
 			on:click={() => {
 			on:click={() => {
-				selectedTab = 'code-interpreter';
+				selectedTab = 'code-execution';
 			}}
 			}}
 		>
 		>
 			<div class=" self-center mr-2">
 			<div class=" self-center mr-2">
@@ -212,7 +212,7 @@
 					/>
 					/>
 				</svg>
 				</svg>
 			</div>
 			</div>
-			<div class=" self-center">{$i18n.t('Code Interpreter')}</div>
+			<div class=" self-center">{$i18n.t('Code Execution')}</div>
 		</button>
 		</button>
 
 
 		<button
 		<button
@@ -391,8 +391,8 @@
 					await config.set(await getBackendConfig());
 					await config.set(await getBackendConfig());
 				}}
 				}}
 			/>
 			/>
-		{:else if selectedTab === 'code-interpreter'}
-			<CodeInterpreter
+		{:else if selectedTab === 'code-execution'}
+			<CodeExecution
 				saveHandler={async () => {
 				saveHandler={async () => {
 					toast.success($i18n.t('Settings saved successfully!'));
 					toast.success($i18n.t('Settings saved successfully!'));
 
 

+ 257 - 0
src/lib/components/admin/Settings/CodeExecution.svelte

@@ -0,0 +1,257 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext } from 'svelte';
+	import { getCodeExecutionConfig, setCodeExecutionConfig } from '$lib/apis/configs';
+
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	let config = null;
+
+	let engines = ['pyodide', 'jupyter'];
+
+	const submitHandler = async () => {
+		const res = await setCodeExecutionConfig(localStorage.token, config);
+	};
+
+	onMount(async () => {
+		const res = await getCodeExecutionConfig(localStorage.token);
+
+		if (res) {
+			config = res;
+		}
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		await submitHandler();
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
+		{#if config}
+			<div>
+				<div class="mb-3.5">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
+
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Code Execution Engine')}</div>
+						<div class="flex items-center relative">
+							<select
+								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+								bind:value={config.CODE_EXECUTION_ENGINE}
+								placeholder={$i18n.t('Select a engine')}
+								required
+							>
+								<option disabled selected value="">{$i18n.t('Select a engine')}</option>
+								{#each engines as engine}
+									<option value={engine}>{engine}</option>
+								{/each}
+							</select>
+						</div>
+					</div>
+
+					{#if config.CODE_EXECUTION_ENGINE === 'jupyter'}
+						<div class="mt-1 flex flex-col gap-1.5 mb-1 w-full">
+							<div class="text-xs font-medium">
+								{$i18n.t('Jupyter URL')}
+							</div>
+
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										class="w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-hidden"
+										type="text"
+										placeholder={$i18n.t('Enter Jupyter URL')}
+										bind:value={config.CODE_EXECUTION_JUPYTER_URL}
+										autocomplete="off"
+									/>
+								</div>
+							</div>
+						</div>
+
+						<div class="mt-1 flex gap-2 mb-1 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>
+						</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}
+								</div>
+							</div>
+						{/if}
+					{/if}
+				</div>
+
+				<div class="mb-3.5">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Code Interpreter')}</div>
+
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+					<div>
+						<div class=" py-0.5 flex w-full justify-between">
+							<div class=" self-center text-xs font-medium">
+								{$i18n.t('Enable Code Interpreter')}
+							</div>
+
+							<Switch bind:state={config.ENABLE_CODE_INTERPRETER} />
+						</div>
+					</div>
+
+					{#if config.ENABLE_CODE_INTERPRETER}
+						<div class=" py-0.5 flex w-full justify-between">
+							<div class=" self-center text-xs font-medium">
+								{$i18n.t('Code Interpreter Engine')}
+							</div>
+							<div class="flex items-center relative">
+								<select
+									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+									bind:value={config.CODE_INTERPRETER_ENGINE}
+									placeholder={$i18n.t('Select a engine')}
+									required
+								>
+									<option disabled selected value="">{$i18n.t('Select a engine')}</option>
+									{#each engines as engine}
+										<option value={engine}>{engine}</option>
+									{/each}
+								</select>
+							</div>
+						</div>
+
+						{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
+							<div class="mt-1 flex flex-col gap-1.5 mb-1 w-full">
+								<div class="text-xs font-medium">
+									{$i18n.t('Jupyter URL')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-hidden"
+											type="text"
+											placeholder={$i18n.t('Enter Jupyter URL')}
+											bind:value={config.CODE_INTERPRETER_JUPYTER_URL}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+
+							<div class="mt-1 flex gap-2 mb-1 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}
+						{/if}
+
+						<hr class="border-gray-100 dark:border-gray-850 my-2" />
+
+						<div>
+							<div class="py-0.5 w-full">
+								<div class=" mb-2.5 text-xs font-medium">
+									{$i18n.t('Code Interpreter Prompt Template')}
+								</div>
+
+								<Tooltip
+									content={$i18n.t(
+										'Leave empty to use the default prompt, or enter a custom prompt'
+									)}
+									placement="top-start"
+								>
+									<Textarea
+										bind:value={config.CODE_INTERPRETER_PROMPT_TEMPLATE}
+										placeholder={$i18n.t(
+											'Leave empty to use the default prompt, or enter a custom prompt'
+										)}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+					{/if}
+				</div>
+			</div>
+		{/if}
+	</div>
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>

+ 0 - 166
src/lib/components/admin/Settings/CodeInterpreter.svelte

@@ -1,166 +0,0 @@
-<script lang="ts">
-	import { toast } from 'svelte-sonner';
-	import { onMount, getContext } from 'svelte';
-	import { getCodeInterpreterConfig, setCodeInterpreterConfig } from '$lib/apis/configs';
-
-	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
-
-	import Tooltip from '$lib/components/common/Tooltip.svelte';
-	import Textarea from '$lib/components/common/Textarea.svelte';
-	import Switch from '$lib/components/common/Switch.svelte';
-
-	const i18n = getContext('i18n');
-
-	export let saveHandler: Function;
-
-	let config = null;
-
-	let engines = ['pyodide', 'jupyter'];
-
-	const submitHandler = async () => {
-		const res = await setCodeInterpreterConfig(localStorage.token, config);
-	};
-
-	onMount(async () => {
-		const res = await getCodeInterpreterConfig(localStorage.token);
-
-		if (res) {
-			config = res;
-		}
-	});
-</script>
-
-<form
-	class="flex flex-col h-full justify-between space-y-3 text-sm"
-	on:submit|preventDefault={async () => {
-		await submitHandler();
-		saveHandler();
-	}}
->
-	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
-		{#if config}
-			<div>
-				<div class=" mb-1 text-sm font-medium">
-					{$i18n.t('Code Interpreter')}
-				</div>
-
-				<div>
-					<div class=" py-0.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">
-							{$i18n.t('Enable Code Interpreter')}
-						</div>
-
-						<Switch bind:state={config.ENABLE_CODE_INTERPRETER} />
-					</div>
-				</div>
-
-				<div class=" py-0.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Code Interpreter Engine')}</div>
-					<div class="flex items-center relative">
-						<select
-							class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
-							bind:value={config.CODE_INTERPRETER_ENGINE}
-							placeholder={$i18n.t('Select a engine')}
-							required
-						>
-							<option disabled selected value="">{$i18n.t('Select a engine')}</option>
-							{#each engines as engine}
-								<option value={engine}>{engine}</option>
-							{/each}
-						</select>
-					</div>
-				</div>
-
-				{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
-					<div class="mt-1 flex flex-col gap-1.5 mb-1 w-full">
-						<div class="text-xs font-medium">
-							{$i18n.t('Jupyter URL')}
-						</div>
-
-						<div class="flex w-full">
-							<div class="flex-1">
-								<input
-									class="w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-hidden"
-									type="text"
-									placeholder={$i18n.t('Enter Jupyter URL')}
-									bind:value={config.CODE_INTERPRETER_JUPYTER_URL}
-									autocomplete="off"
-								/>
-							</div>
-						</div>
-					</div>
-
-					<div class="mt-1 flex gap-2 mb-1 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}
-				{/if}
-			</div>
-
-			<hr class="border-gray-100 dark:border-gray-850 my-2" />
-
-			<div>
-				<div class="py-0.5 w-full">
-					<div class=" mb-2.5 text-xs font-medium">
-						{$i18n.t('Code Interpreter Prompt Template')}
-					</div>
-
-					<Tooltip
-						content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
-						placement="top-start"
-					>
-						<Textarea
-							bind:value={config.CODE_INTERPRETER_PROMPT_TEMPLATE}
-							placeholder={$i18n.t(
-								'Leave empty to use the default prompt, or enter a custom prompt'
-							)}
-						/>
-					</Tooltip>
-				</div>
-			</div>
-		{/if}
-	</div>
-	<div class="flex justify-end pt-3 text-sm font-medium">
-		<button
-			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
-			type="submit"
-		>
-			{$i18n.t('Save')}
-		</button>
-	</div>
-</form>

+ 2 - 2
src/lib/components/admin/Settings/General.svelte

@@ -231,11 +231,11 @@
 								</a>
 								</a>
 							</div>
 							</div>
 
 
-							<button
+							<!-- <button
 								class="flex-shrink-0 text-xs px-3 py-1.5 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-lg font-medium"
 								class="flex-shrink-0 text-xs px-3 py-1.5 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-lg font-medium"
 							>
 							>
 								{$i18n.t('Activate')}
 								{$i18n.t('Activate')}
-							</button>
+							</button> -->
 						</div>
 						</div>
 					</div>
 					</div>
 				</div>
 				</div>

+ 17 - 1
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -20,6 +20,9 @@
 	import PyodideWorker from '$lib/workers/pyodide.worker?worker';
 	import PyodideWorker from '$lib/workers/pyodide.worker?worker';
 	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
 	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
 	import SvgPanZoom from '$lib/components/common/SVGPanZoom.svelte';
 	import SvgPanZoom from '$lib/components/common/SVGPanZoom.svelte';
+	import { config } from '$lib/stores';
+	import { executeCode } from '$lib/apis/utils';
+	import { toast } from 'svelte-sonner';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -120,7 +123,20 @@
 	};
 	};
 
 
 	const executePython = async (code) => {
 	const executePython = async (code) => {
-		executePythonAsWorker(code);
+		if ($config?.code?.engine === 'jupyter') {
+			const output = await executeCode(localStorage.token, code).catch((error) => {
+				toast.error(`${error}`);
+				return null;
+			});
+
+			if (output) {
+				stdout = output.stdout;
+				stderr = output.stderr;
+				result = output.result;
+			}
+		} else {
+			executePythonAsWorker(code);
+		}
 	};
 	};
 
 
 	const executePythonAsWorker = async (code) => {
 	const executePythonAsWorker = async (code) => {

+ 1 - 1
src/lib/components/chat/Settings/Account.svelte

@@ -214,7 +214,7 @@
 						<button
 						<button
 							class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
 							class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
 							on:click={async () => {
 							on:click={async () => {
-								const url = await getGravatarUrl($user.email);
+								const url = await getGravatarUrl(localStorage.token, $user.email);
 
 
 								profileImageUrl = url;
 								profileImageUrl = url;
 							}}>{$i18n.t('Use Gravatar')}</button
 							}}>{$i18n.t('Use Gravatar')}</button

+ 1 - 1
src/lib/components/common/CodeEditor.svelte

@@ -63,7 +63,7 @@
 
 
 	export const formatPythonCodeHandler = async () => {
 	export const formatPythonCodeHandler = async () => {
 		if (codeEditor) {
 		if (codeEditor) {
-			const res = await formatPythonCode(_value).catch((error) => {
+			const res = await formatPythonCode(localStorage.token, _value).catch((error) => {
 				toast.error(`${error}`);
 				toast.error(`${error}`);
 				return null;
 				return null;
 			});
 			});

+ 1 - 1
src/lib/components/layout/Navbar/Menu.svelte

@@ -60,7 +60,7 @@
 	const downloadPdf = async () => {
 	const downloadPdf = async () => {
 		const history = chat.chat.history;
 		const history = chat.chat.history;
 		const messages = createMessagesList(history, history.currentId);
 		const messages = createMessagesList(history, history.currentId);
-		const blob = await downloadChatAsPDF(chat.chat.title, messages);
+		const blob = await downloadChatAsPDF(localStorage.token, chat.chat.title, messages);
 
 
 		// Create a URL for the blob
 		// Create a URL for the blob
 		const url = window.URL.createObjectURL(blob);
 		const url = window.URL.createObjectURL(blob);

+ 1 - 1
src/lib/components/layout/Sidebar/ChatMenu.svelte

@@ -83,7 +83,7 @@
 
 
 		const history = chat.chat.history;
 		const history = chat.chat.history;
 		const messages = createMessagesList(history, history.currentId);
 		const messages = createMessagesList(history, history.currentId);
-		const blob = await downloadChatAsPDF(chat.chat.title, messages);
+		const blob = await downloadChatAsPDF(localStorage.token, chat.chat.title, messages);
 
 
 		// Create a URL for the blob
 		// Create a URL for the blob
 		const url = window.URL.createObjectURL(blob);
 		const url = window.URL.createObjectURL(blob);