mirror of
https://github.com/open-webui/open-webui
synced 2025-04-25 16:49:46 +00:00
enh: code interpreter jupyter support
This commit is contained in:
parent
29c781875d
commit
f9c5819314
@ -1326,6 +1326,48 @@ Your task is to synthesize these responses into a single, high-quality response.
|
|||||||
Responses from models: {{responses}}"""
|
Responses from models: {{responses}}"""
|
||||||
|
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Code Interpreter
|
||||||
|
####################################
|
||||||
|
|
||||||
|
ENABLE_CODE_INTERPRETER = PersistentConfig(
|
||||||
|
"ENABLE_CODE_INTERPRETER",
|
||||||
|
"code_interpreter.enable",
|
||||||
|
os.environ.get("ENABLE_CODE_INTERPRETER", "True").lower() == "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
CODE_INTERPRETER_ENGINE = PersistentConfig(
|
||||||
|
"CODE_INTERPRETER_ENGINE",
|
||||||
|
"code_interpreter.engine",
|
||||||
|
os.environ.get("CODE_INTERPRETER_ENGINE", "pyodide"),
|
||||||
|
)
|
||||||
|
|
||||||
|
CODE_INTERPRETER_JUPYTER_URL = PersistentConfig(
|
||||||
|
"CODE_INTERPRETER_JUPYTER_URL",
|
||||||
|
"code_interpreter.jupyter.url",
|
||||||
|
os.environ.get("CODE_INTERPRETER_JUPYTER_URL", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH = PersistentConfig(
|
||||||
|
"CODE_INTERPRETER_JUPYTER_AUTH",
|
||||||
|
"code_interpreter.jupyter.auth",
|
||||||
|
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = PersistentConfig(
|
||||||
|
"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN",
|
||||||
|
"code_interpreter.jupyter.auth_token",
|
||||||
|
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_TOKEN", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = PersistentConfig(
|
||||||
|
"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD",
|
||||||
|
"code_interpreter.jupyter.auth_password",
|
||||||
|
os.environ.get("CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CODE_INTERPRETER_PROMPT = """
|
DEFAULT_CODE_INTERPRETER_PROMPT = """
|
||||||
#### Tools Available
|
#### Tools Available
|
||||||
|
|
||||||
|
@ -97,6 +97,13 @@ from open_webui.config import (
|
|||||||
OPENAI_API_BASE_URLS,
|
OPENAI_API_BASE_URLS,
|
||||||
OPENAI_API_KEYS,
|
OPENAI_API_KEYS,
|
||||||
OPENAI_API_CONFIGS,
|
OPENAI_API_CONFIGS,
|
||||||
|
# Code Interpreter
|
||||||
|
ENABLE_CODE_INTERPRETER,
|
||||||
|
CODE_INTERPRETER_ENGINE,
|
||||||
|
CODE_INTERPRETER_JUPYTER_URL,
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH,
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||||
# Image
|
# Image
|
||||||
AUTOMATIC1111_API_AUTH,
|
AUTOMATIC1111_API_AUTH,
|
||||||
AUTOMATIC1111_BASE_URL,
|
AUTOMATIC1111_BASE_URL,
|
||||||
@ -570,6 +577,23 @@ app.state.EMBEDDING_FUNCTION = get_embedding_function(
|
|||||||
app.state.config.RAG_EMBEDDING_BATCH_SIZE,
|
app.state.config.RAG_EMBEDDING_BATCH_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
#
|
||||||
|
# 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_JUPYTER_URL = CODE_INTERPRETER_JUPYTER_URL
|
||||||
|
app.state.config.CODE_INTERPRETER_JUPYTER_AUTH = CODE_INTERPRETER_JUPYTER_AUTH
|
||||||
|
app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = (
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN
|
||||||
|
)
|
||||||
|
app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
#
|
#
|
||||||
@ -755,6 +779,7 @@ app.include_router(openai.router, prefix="/openai", tags=["openai"])
|
|||||||
app.include_router(pipelines.router, prefix="/api/v1/pipelines", tags=["pipelines"])
|
app.include_router(pipelines.router, prefix="/api/v1/pipelines", tags=["pipelines"])
|
||||||
app.include_router(tasks.router, prefix="/api/v1/tasks", tags=["tasks"])
|
app.include_router(tasks.router, prefix="/api/v1/tasks", tags=["tasks"])
|
||||||
app.include_router(images.router, prefix="/api/v1/images", tags=["images"])
|
app.include_router(images.router, prefix="/api/v1/images", tags=["images"])
|
||||||
|
|
||||||
app.include_router(audio.router, prefix="/api/v1/audio", tags=["audio"])
|
app.include_router(audio.router, prefix="/api/v1/audio", tags=["audio"])
|
||||||
app.include_router(retrieval.router, prefix="/api/v1/retrieval", tags=["retrieval"])
|
app.include_router(retrieval.router, prefix="/api/v1/retrieval", tags=["retrieval"])
|
||||||
|
|
||||||
|
@ -36,6 +36,61 @@ async def export_config(user=Depends(get_admin_user)):
|
|||||||
return get_config()
|
return get_config()
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# CodeInterpreterConfig
|
||||||
|
############################
|
||||||
|
class CodeInterpreterConfigForm(BaseModel):
|
||||||
|
ENABLE_CODE_INTERPRETER: bool
|
||||||
|
CODE_INTERPRETER_ENGINE: str
|
||||||
|
CODE_INTERPRETER_JUPYTER_URL: Optional[str]
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH: Optional[str]
|
||||||
|
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: 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)):
|
||||||
|
return {
|
||||||
|
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
|
||||||
|
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
|
||||||
|
"CODE_INTERPRETER_JUPYTER_URL": request.app.state.config.CODE_INTERPRETER_JUPYTER_URL,
|
||||||
|
"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_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/code_interpreter", response_model=CodeInterpreterConfigForm)
|
||||||
|
async def set_code_interpreter_config(
|
||||||
|
request: Request, form_data: CodeInterpreterConfigForm, user=Depends(get_admin_user)
|
||||||
|
):
|
||||||
|
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_JUPYTER_URL = (
|
||||||
|
form_data.CODE_INTERPRETER_JUPYTER_URL
|
||||||
|
)
|
||||||
|
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH = (
|
||||||
|
form_data.CODE_INTERPRETER_JUPYTER_AUTH
|
||||||
|
)
|
||||||
|
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = (
|
||||||
|
form_data.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN
|
||||||
|
)
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
|
||||||
|
form_data.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
|
||||||
|
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
|
||||||
|
"CODE_INTERPRETER_JUPYTER_URL": request.app.state.config.CODE_INTERPRETER_JUPYTER_URL,
|
||||||
|
"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_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# SetDefaultModels
|
# SetDefaultModels
|
||||||
############################
|
############################
|
||||||
|
153
backend/open_webui/utils/code_interpreter.py
Normal file
153
backend/open_webui/utils/code_interpreter.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
import websockets
|
||||||
|
import requests
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
|
||||||
|
async def execute_code_jupyter(
|
||||||
|
jupyter_url, code, token=None, password=None, timeout=10
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Executes Python code in a Jupyter kernel.
|
||||||
|
Supports authentication with a token or password.
|
||||||
|
:param jupyter_url: Jupyter server URL (e.g., "http://localhost:8888")
|
||||||
|
:param code: Code to execute
|
||||||
|
:param token: Jupyter authentication token (optional)
|
||||||
|
:param password: Jupyter password (optional)
|
||||||
|
:param timeout: WebSocket timeout in seconds (default: 10s)
|
||||||
|
:return: Dictionary with stdout, stderr, and result
|
||||||
|
"""
|
||||||
|
session = requests.Session() # Maintain cookies
|
||||||
|
headers = {} # Headers for requests
|
||||||
|
|
||||||
|
# Authenticate using password
|
||||||
|
if password and not token:
|
||||||
|
try:
|
||||||
|
login_url = urljoin(jupyter_url, "/login")
|
||||||
|
response = session.get(login_url)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# Retrieve `_xsrf` token
|
||||||
|
xsrf_token = session.cookies.get("_xsrf")
|
||||||
|
if not xsrf_token:
|
||||||
|
raise ValueError("Failed to fetch _xsrf token")
|
||||||
|
|
||||||
|
# Send login request
|
||||||
|
login_data = {"_xsrf": xsrf_token, "password": password}
|
||||||
|
login_response = session.post(
|
||||||
|
login_url, data=login_data, cookies=session.cookies
|
||||||
|
)
|
||||||
|
login_response.raise_for_status()
|
||||||
|
|
||||||
|
# Update headers with `_xsrf`
|
||||||
|
headers["X-XSRFToken"] = xsrf_token
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": f"Authentication Error: {str(e)}",
|
||||||
|
"result": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Construct API URLs with authentication token if provided
|
||||||
|
params = f"?token={token}" if token else ""
|
||||||
|
kernel_url = urljoin(jupyter_url, f"/api/kernels{params}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Include cookies if authenticating with password
|
||||||
|
response = session.post(kernel_url, headers=headers, cookies=session.cookies)
|
||||||
|
response.raise_for_status()
|
||||||
|
kernel_id = response.json()["id"]
|
||||||
|
|
||||||
|
# Construct WebSocket URL
|
||||||
|
websocket_url = urljoin(
|
||||||
|
jupyter_url.replace("http", "ws"),
|
||||||
|
f"/api/kernels/{kernel_id}/channels{params}",
|
||||||
|
)
|
||||||
|
|
||||||
|
# **IMPORTANT:** Include authentication cookies for WebSockets
|
||||||
|
ws_headers = {}
|
||||||
|
if password and not token:
|
||||||
|
ws_headers["X-XSRFToken"] = session.cookies.get("_xsrf")
|
||||||
|
cookies = {name: value for name, value in session.cookies.items()}
|
||||||
|
ws_headers["Cookie"] = "; ".join(
|
||||||
|
[f"{name}={value}" for name, value in cookies.items()]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Connect to the WebSocket
|
||||||
|
async with websockets.connect(
|
||||||
|
websocket_url, additional_headers=ws_headers
|
||||||
|
) as ws:
|
||||||
|
msg_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Send execution request
|
||||||
|
execute_request = {
|
||||||
|
"header": {
|
||||||
|
"msg_id": msg_id,
|
||||||
|
"msg_type": "execute_request",
|
||||||
|
"username": "user",
|
||||||
|
"session": str(uuid.uuid4()),
|
||||||
|
"date": "",
|
||||||
|
"version": "5.3",
|
||||||
|
},
|
||||||
|
"parent_header": {},
|
||||||
|
"metadata": {},
|
||||||
|
"content": {
|
||||||
|
"code": code,
|
||||||
|
"silent": False,
|
||||||
|
"store_history": True,
|
||||||
|
"user_expressions": {},
|
||||||
|
"allow_stdin": False,
|
||||||
|
"stop_on_error": True,
|
||||||
|
},
|
||||||
|
"channel": "shell",
|
||||||
|
}
|
||||||
|
await ws.send(json.dumps(execute_request))
|
||||||
|
|
||||||
|
# Collect execution results
|
||||||
|
stdout, stderr, result = "", "", None
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
message = await asyncio.wait_for(ws.recv(), timeout)
|
||||||
|
message_data = json.loads(message)
|
||||||
|
if message_data.get("parent_header", {}).get("msg_id") == msg_id:
|
||||||
|
msg_type = message_data.get("msg_type")
|
||||||
|
if msg_type == "stream":
|
||||||
|
if message_data["content"]["name"] == "stdout":
|
||||||
|
stdout += message_data["content"]["text"]
|
||||||
|
elif message_data["content"]["name"] == "stderr":
|
||||||
|
stderr += message_data["content"]["text"]
|
||||||
|
elif msg_type in ("execute_result", "display_data"):
|
||||||
|
result = message_data["content"]["data"].get(
|
||||||
|
"text/plain", ""
|
||||||
|
)
|
||||||
|
elif msg_type == "error":
|
||||||
|
stderr += "\n".join(message_data["content"]["traceback"])
|
||||||
|
elif (
|
||||||
|
msg_type == "status"
|
||||||
|
and message_data["content"]["execution_state"] == "idle"
|
||||||
|
):
|
||||||
|
break
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
stderr += "\nExecution timed out."
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
return {"stdout": "", "stderr": f"Error: {str(e)}", "result": ""}
|
||||||
|
finally:
|
||||||
|
# Shutdown the kernel
|
||||||
|
if kernel_id:
|
||||||
|
requests.delete(
|
||||||
|
f"{kernel_url}/{kernel_id}", headers=headers, cookies=session.cookies
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"stdout": stdout.strip(),
|
||||||
|
"stderr": stderr.strip(),
|
||||||
|
"result": result.strip() if result else "",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Example Usage
|
||||||
|
# asyncio.run(execute_code_jupyter("http://localhost:8888", "print('Hello, world!')", token="your-token"))
|
||||||
|
# asyncio.run(execute_code_jupyter("http://localhost:8888", "print('Hello, world!')", password="your-password"))
|
@ -72,7 +72,7 @@ from open_webui.utils.filter import (
|
|||||||
get_sorted_filter_ids,
|
get_sorted_filter_ids,
|
||||||
process_filter_functions,
|
process_filter_functions,
|
||||||
)
|
)
|
||||||
|
from open_webui.utils.code_interpreter import execute_code_jupyter
|
||||||
|
|
||||||
from open_webui.tasks import create_task
|
from open_webui.tasks import create_task
|
||||||
|
|
||||||
@ -1651,15 +1651,45 @@ async def process_chat_response(
|
|||||||
output = ""
|
output = ""
|
||||||
try:
|
try:
|
||||||
if content_blocks[-1]["attributes"].get("type") == "code":
|
if content_blocks[-1]["attributes"].get("type") == "code":
|
||||||
output = await event_caller(
|
code = content_blocks[-1]["content"]
|
||||||
{
|
|
||||||
"type": "execute:python",
|
if (
|
||||||
"data": {
|
request.app.state.config.CODE_INTERPRETER_ENGINE
|
||||||
"id": str(uuid4()),
|
== "pyodide"
|
||||||
"code": content_blocks[-1]["content"],
|
):
|
||||||
},
|
output = await event_caller(
|
||||||
|
{
|
||||||
|
"type": "execute:python",
|
||||||
|
"data": {
|
||||||
|
"id": str(uuid4()),
|
||||||
|
"code": code,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
request.app.state.config.CODE_INTERPRETER_ENGINE
|
||||||
|
== "jupyter"
|
||||||
|
):
|
||||||
|
output = await execute_code_jupyter(
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_URL,
|
||||||
|
code,
|
||||||
|
(
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN
|
||||||
|
if request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH
|
||||||
|
== "token"
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
(
|
||||||
|
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
|
||||||
|
if request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH
|
||||||
|
== "password"
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
output = {
|
||||||
|
"stdout": "Code interpreter engine not configured."
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(output, dict):
|
if isinstance(output, dict):
|
||||||
stdout = output.get("stdout", "")
|
stdout = output.get("stdout", "")
|
||||||
|
@ -58,6 +58,63 @@ export const exportConfig = async (token: string) => {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCodeInterpreterConfig = async (token: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setCodeInterpreterConfig = async (token: string, config: object) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/code_interpreter`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
...config
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
export const getModelsConfig = async (token: string) => {
|
export const getModelsConfig = async (token: string) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
|
@ -19,6 +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';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
@ -188,6 +189,32 @@
|
|||||||
<div class=" self-center">{$i18n.t('Web Search')}</div>
|
<div class=" self-center">{$i18n.t('Web Search')}</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||||
|
'code-interpreter'
|
||||||
|
? ''
|
||||||
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
|
on:click={() => {
|
||||||
|
selectedTab = 'code-interpreter';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" self-center mr-2">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm2.22 1.97a.75.75 0 0 0 0 1.06l.97.97-.97.97a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 0 0-1.06 0ZM8.75 8.5a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class=" self-center">{$i18n.t('Code Interpreter')}</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
<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 ===
|
||||||
'interface'
|
'interface'
|
||||||
@ -364,6 +391,15 @@
|
|||||||
await config.set(await getBackendConfig());
|
await config.set(await getBackendConfig());
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{:else if selectedTab === 'code-interpreter'}
|
||||||
|
<CodeInterpreter
|
||||||
|
saveHandler={async () => {
|
||||||
|
toast.success($i18n.t('Settings saved successfully!'));
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
await config.set(await getBackendConfig());
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{:else if selectedTab === 'interface'}
|
{:else if selectedTab === 'interface'}
|
||||||
<Interface
|
<Interface
|
||||||
on:save={() => {
|
on:save={() => {
|
||||||
|
145
src/lib/components/admin/Settings/CodeInterpreter.svelte
Normal file
145
src/lib/components/admin/Settings/CodeInterpreter.svelte
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getRAGConfig, updateRAGConfig } from '$lib/apis/retrieval';
|
||||||
|
import Switch from '$lib/components/common/Switch.svelte';
|
||||||
|
|
||||||
|
import { models } from '$lib/stores';
|
||||||
|
import { onMount, getContext } from 'svelte';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
||||||
|
import { getCodeInterpreterConfig, setCodeInterpreterConfig } from '$lib/apis/configs';
|
||||||
|
|
||||||
|
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 px-2 p-1 text-xs bg-transparent outline-none 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 Kernel Gateway 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-none"
|
||||||
|
type="text"
|
||||||
|
placeholder={$i18n.t('Enter Jupyter Kernel Gateway 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 Kernel Gateway Auth')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<select
|
||||||
|
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none 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 Kernel Gateway Password')}
|
||||||
|
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<SensitiveInput
|
||||||
|
type="text"
|
||||||
|
placeholder={$i18n.t('Enter Jupyter Kernel Gateway Token')}
|
||||||
|
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <hr class=" dark:border-gray-850 my-2" /> -->
|
||||||
|
{/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>
|
Loading…
Reference in New Issue
Block a user