Skip to content

Commit 2f75eef

Browse files
committed
enh: code execution settings
1 parent 3df6fa7 commit 2f75eef

File tree

15 files changed

+477
-212
lines changed

15 files changed

+477
-212
lines changed

backend/open_webui/config.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,39 @@ class BannerModel(BaseModel):
13771377
# Code Interpreter
13781378
####################################
13791379

1380+
1381+
CODE_EXECUTION_ENGINE = PersistentConfig(
1382+
"CODE_EXECUTION_ENGINE",
1383+
"code_execution.engine",
1384+
os.environ.get("CODE_EXECUTION_ENGINE", "pyodide"),
1385+
)
1386+
1387+
CODE_EXECUTION_JUPYTER_URL = PersistentConfig(
1388+
"CODE_EXECUTION_JUPYTER_URL",
1389+
"code_execution.jupyter.url",
1390+
os.environ.get("CODE_EXECUTION_JUPYTER_URL", ""),
1391+
)
1392+
1393+
CODE_EXECUTION_JUPYTER_AUTH = PersistentConfig(
1394+
"CODE_EXECUTION_JUPYTER_AUTH",
1395+
"code_execution.jupyter.auth",
1396+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH", ""),
1397+
)
1398+
1399+
CODE_EXECUTION_JUPYTER_AUTH_TOKEN = PersistentConfig(
1400+
"CODE_EXECUTION_JUPYTER_AUTH_TOKEN",
1401+
"code_execution.jupyter.auth_token",
1402+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_TOKEN", ""),
1403+
)
1404+
1405+
1406+
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = PersistentConfig(
1407+
"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD",
1408+
"code_execution.jupyter.auth_password",
1409+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", ""),
1410+
)
1411+
1412+
13801413
ENABLE_CODE_INTERPRETER = PersistentConfig(
13811414
"ENABLE_CODE_INTERPRETER",
13821415
"code_interpreter.enable",
@@ -1398,26 +1431,37 @@ class BannerModel(BaseModel):
13981431
CODE_INTERPRETER_JUPYTER_URL = PersistentConfig(
13991432
"CODE_INTERPRETER_JUPYTER_URL",
14001433
"code_interpreter.jupyter.url",
1401-
os.environ.get("CODE_INTERPRETER_JUPYTER_URL", ""),
1434+
os.environ.get(
1435+
"CODE_INTERPRETER_JUPYTER_URL", os.environ.get("CODE_EXECUTION_JUPYTER_URL", "")
1436+
),
14021437
)
14031438

14041439
CODE_INTERPRETER_JUPYTER_AUTH = PersistentConfig(
14051440
"CODE_INTERPRETER_JUPYTER_AUTH",
14061441
"code_interpreter.jupyter.auth",
1407-
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH", ""),
1442+
os.environ.get(
1443+
"CODE_INTERPRETER_JUPYTER_AUTH",
1444+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH", ""),
1445+
),
14081446
)
14091447

14101448
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = PersistentConfig(
14111449
"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
14121450
"code_interpreter.jupyter.auth_token",
1413-
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_TOKEN", ""),
1451+
os.environ.get(
1452+
"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
1453+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_TOKEN", ""),
1454+
),
14141455
)
14151456

14161457

14171458
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = PersistentConfig(
14181459
"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
14191460
"code_interpreter.jupyter.auth_password",
1420-
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD", ""),
1461+
os.environ.get(
1462+
"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
1463+
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", ""),
1464+
),
14211465
)
14221466

14231467

backend/open_webui/main.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@
100100
OPENAI_API_CONFIGS,
101101
# Direct Connections
102102
ENABLE_DIRECT_CONNECTIONS,
103-
# Code Interpreter
103+
# Code Execution
104+
CODE_EXECUTION_ENGINE,
105+
CODE_EXECUTION_JUPYTER_URL,
106+
CODE_EXECUTION_JUPYTER_AUTH,
107+
CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
108+
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
104109
ENABLE_CODE_INTERPRETER,
105110
CODE_INTERPRETER_ENGINE,
106111
CODE_INTERPRETER_PROMPT_TEMPLATE,
@@ -613,10 +618,18 @@ async def lifespan(app: FastAPI):
613618

614619
########################################
615620
#
616-
# CODE INTERPRETER
621+
# CODE EXECUTION
617622
#
618623
########################################
619624

625+
app.state.config.CODE_EXECUTION_ENGINE = CODE_EXECUTION_ENGINE
626+
app.state.config.CODE_EXECUTION_JUPYTER_URL = CODE_EXECUTION_JUPYTER_URL
627+
app.state.config.CODE_EXECUTION_JUPYTER_AUTH = CODE_EXECUTION_JUPYTER_AUTH
628+
app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = CODE_EXECUTION_JUPYTER_AUTH_TOKEN
629+
app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
630+
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
631+
)
632+
620633
app.state.config.ENABLE_CODE_INTERPRETER = ENABLE_CODE_INTERPRETER
621634
app.state.config.CODE_INTERPRETER_ENGINE = CODE_INTERPRETER_ENGINE
622635
app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = CODE_INTERPRETER_PROMPT_TEMPLATE
@@ -1120,6 +1133,9 @@ async def get_app_config(request: Request):
11201133
{
11211134
"default_models": app.state.config.DEFAULT_MODELS,
11221135
"default_prompt_suggestions": app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
1136+
"code": {
1137+
"engine": app.state.config.CODE_EXECUTION_ENGINE,
1138+
},
11231139
"audio": {
11241140
"tts": {
11251141
"engine": app.state.config.TTS_ENGINE,

backend/open_webui/routers/configs.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ async def set_direct_connections_config(
7070
# CodeInterpreterConfig
7171
############################
7272
class CodeInterpreterConfigForm(BaseModel):
73+
CODE_EXECUTION_ENGINE: str
74+
CODE_EXECUTION_JUPYTER_URL: Optional[str]
75+
CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
76+
CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
77+
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
7378
ENABLE_CODE_INTERPRETER: bool
7479
CODE_INTERPRETER_ENGINE: str
7580
CODE_INTERPRETER_PROMPT_TEMPLATE: Optional[str]
@@ -79,9 +84,14 @@ class CodeInterpreterConfigForm(BaseModel):
7984
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
8085

8186

82-
@router.get("/code_interpreter", response_model=CodeInterpreterConfigForm)
83-
async def get_code_interpreter_config(request: Request, user=Depends(get_admin_user)):
87+
@router.get("/code_execution", response_model=CodeInterpreterConfigForm)
88+
async def get_code_execution_config(request: Request, user=Depends(get_admin_user)):
8489
return {
90+
"CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
91+
"CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
92+
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
93+
"CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
94+
"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
8595
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
8696
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
8797
"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
92102
}
93103

94104

95-
@router.post("/code_interpreter", response_model=CodeInterpreterConfigForm)
96-
async def set_code_interpreter_config(
105+
@router.post("/code_execution", response_model=CodeInterpreterConfigForm)
106+
async def set_code_execution_config(
97107
request: Request, form_data: CodeInterpreterConfigForm, user=Depends(get_admin_user)
98108
):
109+
110+
request.app.state.config.CODE_EXECUTION_ENGINE = form_data.CODE_EXECUTION_ENGINE
111+
request.app.state.config.CODE_EXECUTION_JUPYTER_URL = (
112+
form_data.CODE_EXECUTION_JUPYTER_URL
113+
)
114+
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH = (
115+
form_data.CODE_EXECUTION_JUPYTER_AUTH
116+
)
117+
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = (
118+
form_data.CODE_EXECUTION_JUPYTER_AUTH_TOKEN
119+
)
120+
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
121+
form_data.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
122+
)
123+
99124
request.app.state.config.ENABLE_CODE_INTERPRETER = form_data.ENABLE_CODE_INTERPRETER
100125
request.app.state.config.CODE_INTERPRETER_ENGINE = form_data.CODE_INTERPRETER_ENGINE
101126
request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE = (
@@ -118,6 +143,11 @@ async def set_code_interpreter_config(
118143
)
119144

120145
return {
146+
"CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
147+
"CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
148+
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
149+
"CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
150+
"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
121151
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
122152
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
123153
"CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,

backend/open_webui/routers/utils.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,75 @@
44
from open_webui.models.chats import ChatTitleMessagesForm
55
from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
66
from open_webui.constants import ERROR_MESSAGES
7-
from fastapi import APIRouter, Depends, HTTPException, Response, status
7+
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
88
from pydantic import BaseModel
99
from starlette.responses import FileResponse
10+
11+
1012
from open_webui.utils.misc import get_gravatar_url
1113
from open_webui.utils.pdf_generator import PDFGenerator
12-
from open_webui.utils.auth import get_admin_user
14+
from open_webui.utils.auth import get_admin_user, get_verified_user
15+
from open_webui.utils.code_interpreter import execute_code_jupyter
16+
1317

1418
router = APIRouter()
1519

1620

1721
@router.get("/gravatar")
18-
async def get_gravatar(
19-
email: str,
20-
):
22+
async def get_gravatar(email: str, user=Depends(get_verified_user)):
2123
return get_gravatar_url(email)
2224

2325

24-
class CodeFormatRequest(BaseModel):
26+
class CodeForm(BaseModel):
2527
code: str
2628

2729

2830
@router.post("/code/format")
29-
async def format_code(request: CodeFormatRequest):
31+
async def format_code(form_data: CodeForm, user=Depends(get_verified_user)):
3032
try:
31-
formatted_code = black.format_str(request.code, mode=black.Mode())
33+
formatted_code = black.format_str(form_data.code, mode=black.Mode())
3234
return {"code": formatted_code}
3335
except black.NothingChanged:
34-
return {"code": request.code}
36+
return {"code": form_data.code}
3537
except Exception as e:
3638
raise HTTPException(status_code=400, detail=str(e))
3739

3840

41+
@router.post("/code/execute")
42+
async def execute_code(
43+
request: Request, form_data: CodeForm, user=Depends(get_verified_user)
44+
):
45+
if request.app.state.config.CODE_EXECUTION_ENGINE == "jupyter":
46+
output = await execute_code_jupyter(
47+
request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
48+
form_data.code,
49+
(
50+
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN
51+
if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "token"
52+
else None
53+
),
54+
(
55+
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
56+
if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "password"
57+
else None
58+
),
59+
)
60+
61+
return output
62+
else:
63+
raise HTTPException(
64+
status_code=400,
65+
detail="Code execution engine not supported",
66+
)
67+
68+
3969
class MarkdownForm(BaseModel):
4070
md: str
4171

4272

4373
@router.post("/markdown")
4474
async def get_html_from_markdown(
45-
form_data: MarkdownForm,
75+
form_data: MarkdownForm, user=Depends(get_verified_user)
4676
):
4777
return {"html": markdown.markdown(form_data.md)}
4878

@@ -54,7 +84,7 @@ class ChatForm(BaseModel):
5484

5585
@router.post("/pdf")
5686
async def download_chat_as_pdf(
57-
form_data: ChatTitleMessagesForm,
87+
form_data: ChatTitleMessagesForm, user=Depends(get_verified_user)
5888
):
5989
try:
6090
pdf_bytes = PDFGenerator(form_data).generate_chat_pdf()

src/lib/apis/configs/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ export const setDirectConnectionsConfig = async (token: string, config: object)
115115
return res;
116116
};
117117

118-
export const getCodeInterpreterConfig = async (token: string) => {
118+
export const getCodeExecutionConfig = async (token: string) => {
119119
let error = null;
120120

121-
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
121+
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_execution`, {
122122
method: 'GET',
123123
headers: {
124124
'Content-Type': 'application/json',
@@ -142,10 +142,10 @@ export const getCodeInterpreterConfig = async (token: string) => {
142142
return res;
143143
};
144144

145-
export const setCodeInterpreterConfig = async (token: string, config: object) => {
145+
export const setCodeExecutionConfig = async (token: string, config: object) => {
146146
let error = null;
147147

148-
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
148+
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_execution`, {
149149
method: 'POST',
150150
headers: {
151151
'Content-Type': 'application/json',

0 commit comments

Comments
 (0)