This commit is contained in:
Saifeddine ALOUI 2024-02-17 02:08:14 +01:00
parent 023e35a179
commit 1320331fa8
11 changed files with 132 additions and 89 deletions

View File

@ -1,5 +1,5 @@
# =================== Lord Of Large Language Multimodal Systems Configuration file ===========================
version: 61
version: 63
binding_name: null
model_name: null
@ -7,7 +7,9 @@ show_news_panel: True
# Execution protection
turn_on_code_execution: True
turn_on_code_validation: False
turn_on_code_validation: True
turn_on_open_file_validation: False
turn_on_send_file_validation: False
# Server information
headless_server_mode: False

View File

@ -13,7 +13,7 @@ from pydantic import BaseModel, Field
from starlette.responses import StreamingResponse
from lollms.types import MSG_TYPE
from lollms.main_config import BaseConfig
from lollms.utilities import detect_antiprompt, remove_text_from_string, trace_exception
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
@ -52,24 +52,6 @@ router = APIRouter()
lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance()
def show_yes_no_dialog(title, text):
import tkinter as tk
from tkinter import messagebox
# Create a new Tkinter root window and hide it
root = tk.Tk()
root.withdraw()
# Make the window appear on top
root.attributes('-topmost', True)
# Show the dialog box
result = messagebox.askyesno(title, text)
# Destroy the root window
root.destroy()
return result
class CodeRequest(BaseModel):
code: str = Field(..., description="Code to be executed")
discussion_id: int = Field(..., description="Discussion ID")
@ -155,6 +137,10 @@ async def open_code_folder_in_vs_code(request: OpenCodeFolderInVsCodeRequestMode
if lollmsElfServer.config.host=="0.0.0.0":
return {"status":False,"error":"Open code folder in vscode is blocked when the server is exposed outside for very obvious reasons!"}
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!"}
try:
if request.discussion_id:
ASCIIColors.info("Opening folder:")
@ -197,6 +183,13 @@ async def open_file(file_path: FilePath):
if lollmsElfServer.config.host=="0.0.0.0":
return {"status":False,"error":"Open file is blocked when the server is exposed outside for very obvious reasons!"}
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?"
try:
# Validate the 'path' parameter
path = file_path.path
@ -235,6 +228,10 @@ async def open_code_in_vs_code(vs_code_data: VSCodeData):
if lollmsElfServer.config.host=="0.0.0.0":
return {"status":False,"error":"Open code in vs code is blocked when the server is exposed outside for very obvious reasons!"}
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!"}
try:
discussion_id = vs_code_data.discussion_id
message_id = vs_code_data.message_id
@ -275,6 +272,10 @@ async def open_code_folder(request: FolderRequest):
if lollmsElfServer.config.host=="0.0.0.0":
return {"status":False,"error":"Open code folder is blocked when the server is exposed outside for very obvious reasons!"}
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!"}
try:
if request.discussion_id:
discussion_id = request.discussion_id

View File

@ -60,11 +60,14 @@ async def list_databases():
@router.post("/select_database")
def select_database(data:DatabaseSelectionParameters):
if(".." in data.name):
raise "Detected an attempt of path traversal. Are you kidding me?"
if not data.name.endswith(".db"):
data.name += ".db"
print(f'Selecting database {data.name}')
# Create database object
lollmsElfServer.db = DiscussionsDB(lollmsElfServer.lollms_paths.personal_databases_path/data.name)
lollmsElfServer.db = DiscussionsDB((lollmsElfServer.lollms_paths.personal_databases_path/data.name).resolve())
ASCIIColors.info("Checking discussions database... ",end="")
lollmsElfServer.db.create_tables()
lollmsElfServer.db.add_missing_columns()

View File

@ -8,6 +8,7 @@ description:
"""
from fastapi import APIRouter
from pydantic import Field
from lollms_webui import LOLLMSWebUI
from pydantic import BaseModel
from starlette.responses import StreamingResponse
@ -25,55 +26,69 @@ router = APIRouter()
lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance()
class EditMessageParameters(BaseModel):
client_id :str
id :int
message :str
metadata :dict
client_id: str = Field(..., min_length=1)
id: int = Field(..., gt=0)
message: str = Field(..., min_length=1)
metadata: dict = Field(default={})
@router.get("/edit_message")
def edit_message(client_id:str,id:int, message:str,metadata:dict=None):
message_id = id
new_message = message
metadata = metadata
async def edit_message(edit_params: EditMessageParameters):
client_id = edit_params.client_id
message_id = edit_params.id
new_message = edit_params.message
metadata = edit_params.metadata
try:
lollmsElfServer.connections[client_id]["current_discussion"].edit_message(message_id, new_message,new_metadata=metadata)
lollmsElfServer.connections[client_id]["current_discussion"].edit_message(message_id, new_message, new_metadata=metadata)
return {"status": True}
except Exception as ex:
trace_exception(ex)
return {"status": False, "error":str(ex)}
trace_exception(ex) # Assuming 'trace_exception' function logs the error
return {"status": False, "error": "There was an error editing the message"}
class MessageRankParameters(BaseModel):
client_id :str
id :int
class MessageDeleteParameters(BaseModel):
client_id :str
id :int
client_id: str = Field(..., min_length=1)
id: int = Field(..., gt=0)
@router.get("/message_rank_up")
def message_rank_up(client_id:str, id:int):
discussion_id = id
async def message_rank_up(rank_params: MessageRankParameters):
client_id = rank_params.client_id
discussion_id = rank_params.id
try:
new_rank = lollmsElfServer.connections[client_id]["current_discussion"].message_rank_up(discussion_id)
return {"status": True, "new_rank": new_rank}
except Exception as ex:
return {"status": False, "error":str(ex)}
trace_exception(ex) # Assuming 'trace_exception' function logs the error
return {"status": False, "error": "There was an error ranking up the message"}
@router.get("/message_rank_down")
def message_rank_down(client_id:str, id:int):
discussion_id = id
def message_rank_down(rank_params: MessageRankParameters):
client_id = rank_params.client_id
discussion_id = rank_params.id
try:
new_rank = lollmsElfServer.connections[client_id]["current_discussion"].message_rank_down(discussion_id)
return {"status": True, "new_rank": new_rank}
except Exception as ex:
return {"status": False, "error":str(ex)}
class MessageDeleteParameters(BaseModel):
client_id: str = Field(..., min_length=1)
id: int = Field(..., gt=0)
@router.get("/delete_message")
def delete_message(client_id:str, id:int):
discussion_id = id
async def delete_message(delete_params: MessageDeleteParameters):
client_id = delete_params.client_id
discussion_id = delete_params.id
if lollmsElfServer.connections[client_id]["current_discussion"] is None:
return {"status": False,"message":"No discussion is selected"}
else:
new_rank = lollmsElfServer.connections[client_id]["current_discussion"].delete_message(discussion_id)
ASCIIColors.yellow("Message deleted")
return {"status":True,"new_rank": new_rank}
try:
new_rank = lollmsElfServer.connections[client_id]["current_discussion"].delete_message(discussion_id)
ASCIIColors.yellow("Message deleted")
return {"status":True,"new_rank": new_rank}
except Exception as ex:
trace_exception(ex) # Assuming 'trace_exception' function logs the error
return {"status": False, "error": "There was an error deleting the message"}

View File

@ -8,8 +8,9 @@ description:
"""
from fastapi import APIRouter, Request
from fastapi import HTTPException
from lollms_webui import LOLLMSWebUI
from pydantic import BaseModel
from pydantic import BaseModel, Field
from starlette.responses import StreamingResponse
from lollms.types import MSG_TYPE
from lollms.main_config import BaseConfig
@ -44,55 +45,75 @@ def get_presets():
presets.append(preset)
return presets
class PresetData(BaseModel):
name: str = Field(..., min_length=1)
@router.post("/add_preset")
async def add_preset(request: Request):
async def add_preset(preset_data: PresetData):
"""
Changes current voice
:param request: The HTTP request object.
:return: A JSON response with the status of the operation.
"""
# Get the JSON data from the POST request.
preset_data = request.get_json()
presets_folder = lollmsElfServer.lollms_paths.personal_databases_path/"lollms_playground_presets"
if not presets_folder.exists():
presets_folder.mkdir(exist_ok=True, parents=True)
try:
fn = preset_data["name"].lower().replace(" ","_")
filename = presets_folder/f"{fn}.yaml"
with open(filename, 'w', encoding='utf-8') as file:
yaml.dump(preset_data, file)
return {"status": True}
presets_folder = lollmsElfServer.lollms_paths.personal_databases_path/"lollms_playground_presets"
if not presets_folder.exists():
presets_folder.mkdir(exist_ok=True, parents=True)
# Ensure the name doesn't contain any path manipulation characters
if ".." in preset_data.name or "/" in preset_data.name:
raise HTTPException(status_code=400, detail="Invalid preset name")
fn = preset_data.name.lower().replace(" ","_")
filename = presets_folder/f"{fn}.yaml"
with open(filename, 'w', encoding='utf-8') as file:
yaml.dump(preset_data, file)
return {"status": True}
except Exception as ex:
trace_exception(ex) # Assuming 'trace_exception' function logs the error
return {"status": False, "error": "There was an error adding the preset"}
@router.post("/del_preset")
async def del_preset(request: Request):
async def del_preset(preset_data: PresetData):
"""
Saves a preset to a file.
:param request: The HTTP request object.
:param preset_data: The data of the preset.
:return: A JSON response with the status of the operation.
"""
# Get the JSON data from the POST request.
preset_data = request.get_json()
presets_folder = lollmsElfServer.lollms_paths.personal_databases_path/"lollms_playground_presets"
# TODO : process
return {"status":True}
if preset_data.name is None:
raise HTTPException(status_code=400, detail="Preset name is missing in the request")
# Ensure the name doesn't contain any path manipulation characters
if ".." in preset_data.name or "/" in preset_data.name:
raise HTTPException(status_code=400, detail="Invalid preset name")
presets_file = lollmsElfServer.lollms_paths.personal_databases_path/"lollms_playground_presets"/preset_data.name
try:
presets_file.unlink()
return {"status":True}
except:
return {"status":False}
@router.post("/save_presets")
async def save_presets(request: Request):
async def save_presets(preset_data: PresetData):
"""
Saves a preset to a file.
:param request: The HTTP request object.
:param preset_data: The data of the preset.
:return: A JSON response with the status of the operation.
"""
# Get the JSON data from the POST request.
preset_data = request.get_json()
if preset_data.preset is None:
raise HTTPException(status_code=400, detail="Preset data is missing in the request")
presets_file = lollmsElfServer.lollms_paths.personal_databases_path/"presets.json"
# Save the JSON data to a file.
with open(presets_file, "w") as f:
json.dump(preset_data, f, indent=4)
json.dump(preset_data.preset, f, indent=4)
return {"status":True,"message":"Preset saved successfully!"}

@ -1 +1 @@
Subproject commit 61c2f0ed2ad24c6125309650f0a58fad4a84c02a
Subproject commit 8c40b159c546c0d9a2e09b1659207b3f78043115

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
web/dist/index.html vendored
View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMS WebUI - Welcome</title>
<script type="module" crossorigin src="/assets/index-19afe86a.js"></script>
<link rel="stylesheet" href="/assets/index-015056f5.css">
<script type="module" crossorigin src="/assets/index-458fe287.js"></script>
<link rel="stylesheet" href="/assets/index-94163e69.css">
</head>
<body>
<div id="app"></div>

View File

@ -17,7 +17,8 @@
</button>
<button
type="button"
@click="startRecording"
title="Start audio to audio"
@click="startRecordingAndTranscribing"
:class="{ 'text-green-500': isLesteningToVoice }"
class="w-6 hover:text-secondary duration-75 active:scale-90 cursor-pointer text-red-500"
>
@ -639,7 +640,8 @@ export default {
// This event will be triggered when the voices are loaded
this.voices = this.speechSynthesis.getVoices();
},
read(){
read(){
console.log("READING...")
this.isSynthesizingVoice=true
let ss =this.$refs.mdTextarea.selectionStart
let se =this.$refs.mdTextarea.selectionEnd
@ -864,8 +866,6 @@ export default {
this.is_recording = true;
this.pending = false;
console.log(response.data)
this.presets=response.data
this.selectedPreset = this.presets[0]
}).catch(ex=>{
this.$refs.toast.showToast(`Error: ${ex}`,4,false)
});
@ -889,7 +889,7 @@ export default {
},
startRecordingAndTranscribing(){
this.pending = true;
if(!this.is_recording){
if(!this.is_deaf_transcribing){
axios.get('/start_recording').then(response => {
this.is_deaf_transcribing = true;
this.pending = false;

View File

@ -4669,6 +4669,7 @@ export default {
try{
let idx = this.$store.state.modelsZoo.findIndex(item => item.name == this.$store.state.selectedModel)
if(idx>=0){
console.log(`model avatar : ${this.$store.state.modelsZoo[idx].avatar}`)
return this.$store.state.modelsZoo[idx].avatar
}
else{