lollms-webui/endpoints/lollms_advanced.py

351 lines
15 KiB
Python
Raw Normal View History

"""
project: lollms_user
file: lollms_user.py
author: ParisNeo
description:
This module contains a set of FastAPI routes that provide information about the Lord of Large Language and Multimodal Systems (LoLLMs) Web UI
application. These routes allow users to do advanced stuff like executing code.
"""
from fastapi import APIRouter, Request
from lollms_webui import LOLLMSWebUI
2024-02-15 00:01:55 +00:00
from pydantic import BaseModel, Field
from starlette.responses import StreamingResponse
from lollms.types import MSG_TYPE
from lollms.main_config import BaseConfig
2024-02-17 01:08:14 +00:00
from lollms.utilities import detect_antiprompt, remove_text_from_string, trace_exception, show_yes_no_dialog
from ascii_colors import ASCIIColors
from api.db import DiscussionsDB
from pathlib import Path
from safe_store.text_vectorizer import TextVectorizer, VectorizationMethod, VisualizationMethod
import tqdm
from fastapi import FastAPI, UploadFile, File
import shutil
2024-01-07 23:22:23 +00:00
import os
import platform
2024-02-14 23:35:04 +00:00
import string
import re
2024-02-15 00:01:55 +00:00
import subprocess
from typing import Optional
2024-02-14 23:35:04 +00:00
# Regular expression pattern to validate file paths
FILE_PATH_REGEX = r'^[a-zA-Z0-9_\-\\\/]+$'
# Function to validate file paths using the regex pattern
def validate_file_path(path):
return re.match(FILE_PATH_REGEX, path)
from utilities.execution_engines.python_execution_engine import execute_python
from utilities.execution_engines.latex_execution_engine import execute_latex
from utilities.execution_engines.shell_execution_engine import execute_bash
2024-02-05 22:50:40 +00:00
from utilities.execution_engines.javascript_execution_engine import execute_javascript
2024-02-05 23:57:00 +00:00
from utilities.execution_engines.html_execution_engine import execute_html
2024-02-05 22:50:40 +00:00
from utilities.execution_engines.mermaid_execution_engine import execute_mermaid
from utilities.execution_engines.graphviz_execution_engine import execute_graphviz
2024-01-09 22:26:41 +00:00
# ----------------------- Defining router and main class ------------------------------
2024-01-07 23:22:23 +00:00
router = APIRouter()
lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance()
2024-02-15 00:31:16 +00:00
2024-02-15 19:31:03 +00:00
class CodeRequest(BaseModel):
code: str = Field(..., description="Code to be executed")
discussion_id: int = Field(..., description="Discussion ID")
message_id: int = Field(..., description="Message ID")
language: str = Field(..., description="Programming language of the code")
@router.post("/execute_code")
2024-02-15 19:31:03 +00:00
async def execute_code(request: CodeRequest):
"""
Executes Python code and returns the output.
:param request: The HTTP request object.
:return: A JSON response with the status of the operation.
"""
2024-02-15 00:31:16 +00:00
2024-02-14 23:35:04 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Code execution is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 19:31:03 +00:00
return {"status":False,"error":"Code execution is blocked when the server is exposed outside for very obvious reasons!"}
2024-02-15 00:31:16 +00:00
if not lollmsElfServer.config.turn_on_code_execution:
return {"status":False,"error":"Code execution is blocked by the configuration!"}
if lollmsElfServer.config.turn_on_code_validation:
if not show_yes_no_dialog("Validation","Do you validate the execution of the code?"):
return {"status":False,"error":"User refused the execution!"}
try:
2024-02-15 19:31:03 +00:00
code = request.code
discussion_id = request.discussion_id
message_id = request.message_id
language = request.language
if language=="python":
2024-02-05 22:50:40 +00:00
ASCIIColors.info("Executing python code:")
ASCIIColors.yellow(code)
return execute_python(code, discussion_id, message_id)
2024-02-05 22:50:40 +00:00
if language=="javascript":
ASCIIColors.info("Executing javascript code:")
ASCIIColors.yellow(code)
return execute_javascript(code, discussion_id, message_id)
2024-02-10 00:32:22 +00:00
if language in ["html","html5","svg"]:
2024-02-05 23:57:00 +00:00
ASCIIColors.info("Executing javascript code:")
ASCIIColors.yellow(code)
return execute_html(code, discussion_id, message_id)
2024-02-15 19:31:03 +00:00
elif language=="latex":
2024-02-05 22:50:40 +00:00
ASCIIColors.info("Executing latex code:")
ASCIIColors.yellow(code)
return execute_latex(code, discussion_id, message_id)
elif language in ["bash","shell","cmd","powershell"]:
2024-02-05 22:50:40 +00:00
ASCIIColors.info("Executing shell code:")
ASCIIColors.yellow(code)
return execute_bash(code, discussion_id, message_id)
2024-02-05 22:50:40 +00:00
elif language in ["mermaid"]:
ASCIIColors.info("Executing mermaid code:")
ASCIIColors.yellow(code)
return execute_mermaid(code, discussion_id, message_id)
elif language in ["graphviz","dot"]:
ASCIIColors.info("Executing graphviz code:")
ASCIIColors.yellow(code)
return execute_graphviz(code, discussion_id, message_id)
2024-02-14 23:35:04 +00:00
return {"status": False, "error": "Unsupported language", "execution_time": 0}
2024-01-07 23:22:23 +00:00
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
2024-02-15 00:01:55 +00:00
class OpenCodeFolderInVsCodeRequestModel(BaseModel):
discussion_id: Optional[int] = Field(None, gt=0)
message_id: Optional[int] = Field(None, gt=0)
code: Optional[str]
folder_path: Optional[str]
2024-01-07 23:22:23 +00:00
@router.post("/open_code_folder_in_vs_code")
2024-02-15 00:01:55 +00:00
async def open_code_folder_in_vs_code(request: OpenCodeFolderInVsCodeRequestModel):
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Open code folder in vscode is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Open code folder in vscode is blocked when the server is exposed outside for very obvious reasons!"}
2024-01-07 23:22:23 +00:00
2024-02-17 01:08:14 +00:00
if lollmsElfServer.config.turn_on_open_file_validation:
if not show_yes_no_dialog("Validation","Do you validate the opening of folder in vscode?"):
return {"status":False,"error":"User refused the execution!"}
2024-01-07 23:22:23 +00:00
try:
2024-02-15 00:01:55 +00:00
if request.discussion_id:
2024-02-04 23:43:23 +00:00
ASCIIColors.info("Opening folder:")
2024-02-15 00:01:55 +00:00
root_folder = lollmsElfServer.lollms_paths.personal_outputs_path/"discussions"/f"d_{request.discussion_id}"
2024-02-04 23:43:23 +00:00
root_folder.mkdir(parents=True,exist_ok=True)
2024-02-15 00:01:55 +00:00
tmp_file = root_folder/f"ai_code_{request.message_id}.py"
2024-02-04 23:43:23 +00:00
with open(tmp_file,"w") as f:
2024-02-15 00:01:55 +00:00
f.write(request.code)
2024-02-04 23:43:23 +00:00
2024-02-15 00:01:55 +00:00
if os.path.isdir(root_folder):
subprocess.run(['code', root_folder], check=True)
elif request.folder_path:
2024-02-04 23:43:23 +00:00
ASCIIColors.info("Opening folder:")
2024-02-15 00:01:55 +00:00
root_folder = request.folder_path
2024-02-04 23:43:23 +00:00
root_folder.mkdir(parents=True,exist_ok=True)
2024-02-15 00:01:55 +00:00
if os.path.isdir(root_folder):
subprocess.run(['code', root_folder], check=True)
2024-01-07 23:22:23 +00:00
2024-02-14 23:35:04 +00:00
return {"status": True, "execution_time": 0}
2024-01-07 23:22:23 +00:00
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
2024-02-15 00:01:55 +00:00
return {"status":False,"error":"An error occurred during processing."}
2024-01-07 23:22:23 +00:00
2024-02-15 00:01:55 +00:00
class FilePath(BaseModel):
path: Optional[str] = Field(None, max_length=500)
2024-01-08 00:08:47 +00:00
@router.post("/open_file")
2024-02-15 00:01:55 +00:00
async def open_file(file_path: FilePath):
2024-01-07 23:22:23 +00:00
"""
Opens code in vs code.
2024-02-15 00:01:55 +00:00
:param file_path: The file path object.
2024-01-07 23:22:23 +00:00
:return: A JSON response with the status of the operation.
"""
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Open file is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Open file is blocked when the server is exposed outside for very obvious reasons!"}
2024-01-07 23:22:23 +00:00
2024-02-17 01:08:14 +00:00
if lollmsElfServer.config.turn_on_open_file_validation:
if not show_yes_no_dialog("Validation","Do you validate the opening of a file?"):
return {"status":False,"error":"User refused the opeining file!"}
if(".." in path):
raise "Detected an attempt of path traversal. Are you kidding me?"
2024-01-07 23:22:23 +00:00
try:
2024-02-14 23:35:04 +00:00
# Validate the 'path' parameter
2024-02-15 00:01:55 +00:00
path = file_path.path
2024-02-14 23:35:04 +00:00
if not validate_file_path(path):
return {"status":False,"error":"Invalid file path"}
# Sanitize the 'path' parameter
path = os.path.realpath(path)
2024-02-15 00:01:55 +00:00
# Use subprocess.Popen to safely open the file
2024-02-15 08:19:47 +00:00
subprocess.Popen(["start", path])
2024-02-14 23:35:04 +00:00
return {"status": True, "execution_time": 0}
2024-01-07 23:22:23 +00:00
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
2024-02-15 00:01:55 +00:00
class VSCodeData(BaseModel):
discussion_id: Optional[int] = Field(None, ge=0)
message_id: Optional[int] = Field(None, ge=0)
code: str = Field(...)
2024-01-07 23:22:23 +00:00
2024-01-08 00:08:47 +00:00
@router.post("/open_code_in_vs_code")
2024-02-15 00:01:55 +00:00
async def open_code_in_vs_code(vs_code_data: VSCodeData):
2024-01-07 23:22:23 +00:00
"""
Opens code in vs code.
2024-02-15 00:01:55 +00:00
:param vs_code_data: The data object.
2024-01-07 23:22:23 +00:00
:return: A JSON response with the status of the operation.
"""
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Open code in vs code is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Open code in vs code is blocked when the server is exposed outside for very obvious reasons!"}
2024-01-07 23:22:23 +00:00
2024-02-17 01:08:14 +00:00
if lollmsElfServer.config.turn_on_open_file_validation:
if not show_yes_no_dialog("Validation","Do you validate the opening of a code in vscode?"):
return {"status":False,"error":"User refused the opeining file!"}
2024-01-07 23:22:23 +00:00
try:
2024-02-15 00:01:55 +00:00
discussion_id = vs_code_data.discussion_id
message_id = vs_code_data.message_id
code = vs_code_data.code
2024-01-07 23:22:23 +00:00
ASCIIColors.info("Opening folder:")
# Create a temporary file.
2024-02-14 23:35:04 +00:00
root_folder = Path(os.path.realpath(lollmsElfServer.lollms_paths.personal_outputs_path/"discussions"/f"d_{discussion_id}"/f"{message_id}.py"))
2024-01-07 23:22:23 +00:00
root_folder.mkdir(parents=True,exist_ok=True)
tmp_file = root_folder/f"ai_code_{message_id}.py"
with open(tmp_file,"w") as f:
f.write(code)
2024-02-15 00:01:55 +00:00
# Use subprocess.Popen to safely open the file
2024-02-15 08:19:47 +00:00
subprocess.Popen(["code", str(root_folder)])
2024-02-15 00:01:55 +00:00
2024-02-14 23:35:04 +00:00
return {"status": True, "execution_time": 0}
2024-01-07 23:22:23 +00:00
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
2024-02-15 00:01:55 +00:00
class FolderRequest(BaseModel):
discussion_id: Optional[int] = Field(None, title="The discussion ID")
folder_path: Optional[str] = Field(None, title="The folder path")
2024-01-07 23:22:23 +00:00
2024-01-08 00:08:47 +00:00
@router.post("/open_code_folder")
2024-02-15 00:01:55 +00:00
async def open_code_folder(request: FolderRequest):
2024-01-07 23:22:23 +00:00
"""
Opens code folder.
:param request: The HTTP request object.
:return: A JSON response with the status of the operation.
"""
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Open code folder is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Open code folder is blocked when the server is exposed outside for very obvious reasons!"}
2024-02-15 00:01:55 +00:00
2024-02-17 01:08:14 +00:00
if lollmsElfServer.config.turn_on_open_file_validation:
if not show_yes_no_dialog("Validation","Do you validate the opening of a folder?"):
return {"status":False,"error":"User refused the opeining folder!"}
2024-01-07 23:22:23 +00:00
try:
2024-02-15 00:01:55 +00:00
if request.discussion_id:
discussion_id = request.discussion_id
2024-02-04 23:43:23 +00:00
ASCIIColors.info("Opening folder:")
# Create a temporary file.
2024-02-14 23:35:04 +00:00
root_folder = lollmsElfServer.lollms_paths.personal_outputs_path / "discussions" / f"d_{discussion_id}"
root_folder.mkdir(parents=True, exist_ok=True)
2024-02-04 23:43:23 +00:00
if platform.system() == 'Windows':
2024-02-15 08:19:47 +00:00
subprocess.run(['start', str(root_folder)], check=True)
2024-02-04 23:43:23 +00:00
elif platform.system() == 'Linux':
2024-02-15 00:01:55 +00:00
subprocess.run(['xdg-open', str(root_folder)], check=True)
2024-02-04 23:43:23 +00:00
elif platform.system() == 'Darwin':
2024-02-15 00:01:55 +00:00
subprocess.run(['open', str(root_folder)], check=True)
2024-02-14 23:35:04 +00:00
return {"status": True, "execution_time": 0}
2024-02-15 00:01:55 +00:00
elif request.folder_path:
folder_path = os.path.realpath(request.folder_path)
2024-02-14 23:35:04 +00:00
# Verify that this is a file and not an executable
root_folder = Path(folder_path)
is_valid_folder_path = root_folder.is_dir()
if not is_valid_folder_path:
return {"status":False, "error":"Invalid folder path"}
2024-02-04 23:43:23 +00:00
ASCIIColors.info("Opening folder:")
# Create a temporary file.
2024-02-14 23:35:04 +00:00
root_folder.mkdir(parents=True, exist_ok=True)
2024-02-04 23:43:23 +00:00
if platform.system() == 'Windows':
2024-02-15 08:19:47 +00:00
subprocess.run(['start', str(root_folder)], check=True)
2024-02-04 23:43:23 +00:00
elif platform.system() == 'Linux':
2024-02-15 00:01:55 +00:00
subprocess.run(['xdg-open', str(root_folder)], check=True)
2024-02-04 23:43:23 +00:00
elif platform.system() == 'Darwin':
2024-02-15 00:01:55 +00:00
subprocess.run(['open', str(root_folder)], check=True)
2024-02-14 23:35:04 +00:00
return {"status": True, "execution_time": 0}
2024-01-07 23:22:23 +00:00
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
2024-02-15 00:01:55 +00:00
return {"status": False, "error": "An error occurred while processing the request"}
2024-02-03 18:04:43 +00:00
@router.get("/start_recording")
def start_recording():
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Start recording is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Start recording is blocked when the server is exposed outside for very obvious reasons!"}
2024-02-03 18:04:43 +00:00
lollmsElfServer.info("Starting audio capture")
try:
from lollms.media import AudioRecorder
lollmsElfServer.rec_output_folder = lollmsElfServer.lollms_paths.personal_outputs_path/"audio_rec"
lollmsElfServer.rec_output_folder.mkdir(exist_ok=True, parents=True)
lollmsElfServer.summoned = False
2024-02-05 19:37:46 +00:00
lollmsElfServer.audio_cap = AudioRecorder(lollmsElfServer.sio,lollmsElfServer.rec_output_folder/"rt.wav", callback=lollmsElfServer.audio_callback,lollmsCom=lollmsElfServer, transcribe=True)
2024-02-03 18:04:43 +00:00
lollmsElfServer.audio_cap.start_recording()
except:
lollmsElfServer.InfoMessage("Couldn't load media library.\nYou will not be able to perform any of the media linked operations. please verify the logs and install any required installations")
@router.get("/stop_recording")
def stop_recording():
2024-02-15 20:47:24 +00:00
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Stop recording is blocked when in headless mode for obvious security reasons!"}
if lollmsElfServer.config.host!="localhost":
2024-02-15 20:47:24 +00:00
return {"status":False,"error":"Stop recording is blocked when the server is exposed outside for very obvious reasons!"}
2024-02-03 18:04:43 +00:00
lollmsElfServer.info("Stopping audio capture")
text = lollmsElfServer.audio_cap.stop_recording()
return text
2024-02-05 22:50:40 +00:00