2024-01-07 22:17:22 +00:00
"""
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
2024-01-07 22:17:22 +00:00
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
2024-02-23 20:20:37 +00:00
from lollms . security import sanitize_path
2024-01-07 22:17:22 +00:00
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 )
2024-01-07 22:17:22 +00:00
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-07 22:17:22 +00:00
2024-01-09 22:26:41 +00:00
# ----------------------- Defining router and main class ------------------------------
2024-01-07 23:22:23 +00:00
2024-01-07 22:17:22 +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 " )
2024-01-07 22:17:22 +00:00
@router.post ( " /execute_code " )
2024-02-15 19:31:03 +00:00
async def execute_code ( request : CodeRequest ) :
2024-01-07 22:17:22 +00:00
"""
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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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-01-07 22:17:22 +00:00
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! " }
2024-01-07 22:17:22 +00:00
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
2024-01-07 22:17:22 +00:00
if language == " python " :
2024-02-05 22:50:40 +00:00
ASCIIColors . info ( " Executing python code: " )
ASCIIColors . yellow ( code )
2024-01-07 22:17:22 +00:00
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
2024-01-07 22:17:22 +00:00
elif language == " latex " :
2024-02-05 22:50:40 +00:00
ASCIIColors . info ( " Executing latex code: " )
ASCIIColors . yellow ( code )
2024-01-07 22:17:22 +00:00
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 )
2024-01-07 22:17:22 +00:00
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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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! " }
2024-02-23 20:20:37 +00:00
sanitize_path ( path )
2024-02-17 01:08:14 +00:00
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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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
2024-01-07 22:17:22 +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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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. \n You 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! " }
2024-02-19 22:03:59 +00:00
if lollmsElfServer . config . host != " localhost " and lollmsElfServer . config . host != " 127.0.0.1 " :
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