This commit is contained in:
Saifeddine ALOUI 2024-05-05 16:01:19 +02:00
parent e63dcb81ad
commit c370b6ecce
15 changed files with 239 additions and 79 deletions

View File

@ -81,7 +81,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
xtts_use_deepspeed: false
xtts_use_streaming_mode: true

View File

@ -78,7 +78,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
auto_read: false
xtts_current_voice: null

View File

@ -78,7 +78,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
auto_read: false
xtts_current_voice: null

View File

@ -78,7 +78,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
auto_read: false
xtts_current_voice: null

View File

@ -665,7 +665,8 @@ class LollmsApplication(LoLLMsCom):
else:
conditionning = self.personality._personality_conditioning
conditionning = self.personality.replace_keys(conditionning, self.personality.conditionning_commands) +"" if conditionning[-1]=="\n" else "\n"
if len(conditionning)>0:
conditionning = self.personality.replace_keys(conditionning, self.personality.conditionning_commands) +"" if conditionning[-1]=="\n" else "\n"
# Check if there are document files to add to the prompt
internet_search_results = ""

View File

@ -81,7 +81,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
xtts_use_deepspeed: false
xtts_use_streaming_mode: true

View File

@ -13,7 +13,7 @@ from lollms.config import InstallOption, TypedConfig, BaseConfig
from lollms.main_config import LOLLMSConfig
from lollms.paths import LollmsPaths
from lollms.binding import LLMBinding, BindingType
from lollms.utilities import PromptReshaper, PackageManager, discussion_path_to_url, process_ai_output
from lollms.utilities import PromptReshaper, PackageManager, discussion_path_to_url, process_ai_output, remove_text_from_string
from lollms.com import NotificationType, NotificationDisplayType
from lollms.client_session import Session, Client
@ -702,7 +702,7 @@ class AIPersonality:
antiprompt = self.detect_antiprompt(bot_says)
if antiprompt:
self.bot_says = self.remove_text_from_string(bot_says,antiprompt)
self.bot_says = remove_text_from_string(bot_says,antiprompt)
ASCIIColors.warning(f"\n{antiprompt} detected. Stopping generation")
return False
else:

View File

@ -81,7 +81,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
xtts_use_deepspeed: false
xtts_use_streaming_mode: true

View File

@ -258,9 +258,6 @@ def remove_file(data:RemoveFileData):
# ------------------------------------------- Languages endpoints ------------------------------------------------
class Identification(BaseModel):
client_id:str
@router.post("/get_personality_languages_list")
def get_current_personality_files_list(data:Identification):
check_access(lollmsElfServer, data.client_id)

View File

@ -14,6 +14,7 @@ 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, find_first_available_file_index, add_period, PackageManager
from lollms.security import check_access
from pathlib import Path
from ascii_colors import ASCIIColors
import os
@ -24,11 +25,19 @@ import platform
router = APIRouter()
lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance()
class Identification(BaseModel):
client_id: str
class ModelPost(BaseModel):
client_id: str
model_url: str
# ----------------------- voice ------------------------------
@router.get("/install_sd")
def install_sd():
@router.post("/install_sd")
# async def your_endpoint(request: Request):
# request_data = await request.json()
# print(request_data) # Use proper logging in real applications
def install_sd(data: Identification):
check_access(lollmsElfServer, data.client_id)
try:
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"}
@ -48,8 +57,9 @@ def install_sd():
return {"status":False, 'error':str(ex)}
@router.get("/upgrade_sd")
def upgrade_sd():
@router.post("/upgrade_sd")
def upgrade_sd(data: Identification):
check_access(lollmsElfServer, data.client_id)
try:
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"}
@ -71,8 +81,9 @@ def upgrade_sd():
@router.get("/start_sd")
def start_sd():
@router.post("/start_sd")
def start_sd(data: Identification):
check_access(lollmsElfServer, data.client_id)
try:
if lollmsElfServer.config.headless_server_mode:
return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"}
@ -91,8 +102,20 @@ def start_sd():
lollmsElfServer.InfoMessage(f"It looks like I could not install SD because of this error:\n{ex}\nThis is commonly caused by a previous version that I couldn't delete. PLease remove {lollmsElfServer.lollms_paths.personal_path}/shared/auto_sd manually then try again")
return {"status":False, 'error':str(ex)}
@router.get("/show_sd")
def show_sd():
@router.post("/show_sd")
def show_sd(data: Identification):
check_access(lollmsElfServer, data.client_id)
import webbrowser
webbrowser.open(lollmsElfServer.config.sd_base_url)
return {"status":True}
return {"status":True}
@router.post("/install_model")
def install_model(data: ModelPost):
check_access(lollmsElfServer, data.client_id)
@router.get("/sd_is_ready")
def show_sd():
if hasattr(lollmsElfServer,'sd') and lollmsElfServer.sd is not None:
if lollmsElfServer.sd.ready:
return {"status":True}
return {"status":False}

View File

@ -93,12 +93,12 @@ async def upload_avatar(avatar: UploadFile = File(...)):
return {"status": True,"fileName": f"{random_filename}{extension}"}
@router.post("/upload_logo")
async def upload_logo(avatar: UploadFile = File(...)):
async def upload_logo(logo: UploadFile = File(...)):
"""
Uploads a user avatar file to a dedicated directory, preventing path traversal attacks.
Parameters:
- avatar: UploadFile object representing the user avatar file.
- logo: UploadFile object representing the user avatar file.
Returns:
- Dictionary with the status of the upload and the generated file name.
@ -107,19 +107,19 @@ async def upload_logo(avatar: UploadFile = File(...)):
- HTTPException with a 400 status code and an error message if the file is invalid or has an invalid type.
"""
# Only allow certain file types
if avatar.filename.endswith((".jpg", ".png")):
if logo.filename.endswith((".jpg", ".png")):
# Create a random file name
random_filename = str(uuid.uuid4())
# Use the file extension of the uploaded file
extension = os.path.splitext(avatar.filename)[1]
extension = os.path.splitext(logo.filename)[1]
# Create the new file path in a dedicated directory
file_location = os.path.join(lollmsElfServer.lollms_paths.personal_user_infos_path, f"{random_filename}{extension}")
try:
# Open the image to check if it's a valid image
img = Image.open(avatar.file)
img = Image.open(logo.file)
# Save the file
img.save(file_location)

View File

@ -78,6 +78,89 @@ async def text2Audio(request: LollmsText2AudioRequest):
"""
Executes Python code and returns the output.
:param request: The HTTP request object.
:return: A JSON response with the status of the operation.
"""
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" and lollmsElfServer.config.host!="127.0.0.1":
return {"status":False,"error":"Code execution is blocked when the server is exposed outside for very obvious reasons!"}
if request.fn:
request.fn = os.path.realpath(str((lollmsElfServer.lollms_paths.personal_outputs_path/"audio_out")/request.fn))
validate_path(request.fn,[str(lollmsElfServer.lollms_paths.personal_outputs_path/"audio_out")])
try:
# Get the JSON data from the POST request.
try:
from lollms.services.xtts.lollms_xtts import LollmsXTTS
voice=lollmsElfServer.config.xtts_current_voice
if lollmsElfServer.tts is None:
voice=lollmsElfServer.config.xtts_current_voice
if voice!="main_voice":
voices_folder = lollmsElfServer.lollms_paths.custom_voices_path
else:
voices_folder = Path(__file__).parent.parent.parent/"services/xtts/voices"
lollmsElfServer.tts = LollmsXTTS(
lollmsElfServer,
voices_folder=voices_folder,
voice_samples_path=Path(__file__).parent/"voices",
xtts_base_url= lollmsElfServer.config.xtts_base_url,
use_deep_speed= lollmsElfServer.config.xtts_use_deep_speed,
use_streaming_mode= lollmsElfServer.config.xtts_use_streaming_mode,
)
except Exception as ex:
return {"url": None, "error":f"{ex}"}
voice=lollmsElfServer.config.xtts_current_voice if request.voice is None else request.voice
index = find_first_available_file_index(lollmsElfServer.tts.output_folder, "voice_sample_",".wav")
output_fn=f"voice_sample_{index}.wav" if request.fn is None else request.fn
if voice is None:
voice = "main_voice"
lollmsElfServer.info("Starting to build voice")
try:
from lollms.services.xtts.lollms_xtts import LollmsXTTS
# If the personality has a voice, then use it
if voice!="main_voice":
voices_folder = lollmsElfServer.lollms_paths.custom_voices_path
else:
voices_folder = Path(__file__).parent.parent.parent/"services/xtts/voices"
if lollmsElfServer.tts is None:
lollmsElfServer.tts = LollmsXTTS(
lollmsElfServer,
voices_folder=voices_folder,
voice_samples_path=Path(__file__).parent/"voices",
xtts_base_url= lollmsElfServer.config.xtts_base_url,
use_deep_speed=lollmsElfServer.config.xtts_use_deepspeed,
use_streaming_mode=lollmsElfServer.config.xtts_use_streaming_mode
)
if lollmsElfServer.tts.ready:
language = lollmsElfServer.config.xtts_current_language# convert_language_name()
lollmsElfServer.tts.set_speaker_folder(voices_folder)
preprocessed_text= add_period(request.text)
voice_file = [v for v in voices_folder.iterdir() if v.stem==voice and v.suffix==".wav"]
if len(voice_file)==0:
return {"status":False,"error":"Voice not found"}
lollmsElfServer.tts.tts_to_audio(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language)
else:
lollmsElfServer.InfoMessage("xtts is not up yet.\nPlease wait for it to load then try again. This may take some time.")
return {"status":False, "error":"Service not ready yet"}
return {"url": url}
except Exception as ex:
trace_exception(ex)
return {"url": None}
except Exception as ex:
trace_exception(ex)
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
@router.post("/text2wav")
async def text2Wav(request: LollmsText2AudioRequest):
"""
Executes Python code and returns the output.
:param request: The HTTP request object.
:return: A JSON response with the status of the operation.
"""
@ -143,11 +226,8 @@ async def text2Audio(request: LollmsText2AudioRequest):
voice_file = [v for v in voices_folder.iterdir() if v.stem==voice and v.suffix==".wav"]
if len(voice_file)==0:
return {"status":False,"error":"Voice not found"}
if not lollmsElfServer.config.xtts_use_streaming_mode:
lollmsElfServer.tts.tts_to_file(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language)
lollmsElfServer.info(f"Voice file ready at {url}")
else:
lollmsElfServer.tts.tts_to_audio(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language)
lollmsElfServer.tts.tts_to_file(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language)
lollmsElfServer.info(f"Voice file ready at {url}")
else:
lollmsElfServer.InfoMessage("xtts is not up yet.\nPlease wait for it to load then try again. This may take some time.")
return {"status":False, "error":"Service not ready yet"}
@ -160,6 +240,7 @@ async def text2Audio(request: LollmsText2AudioRequest):
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
@router.get("/install_xtts")
def install_xtts():
try:
@ -211,6 +292,7 @@ async def upload_voice_file(file: UploadFile = File(...)):
allowed_extensions = {'wav'}
# Use Pathlib to handle the filename
sanitize_path(file.filename)
file_path = Path(file.filename)
file_extension = file_path.suffix[1:].lower()
@ -231,4 +313,12 @@ async def upload_voice_file(file: UploadFile = File(...)):
if lollmsElfServer.config.auto_save:
lollmsElfServer.config.save_config()
return {"message": f"Successfully uploaded {safe_filename}"}
return {"message": f"Successfully uploaded {safe_filename}"}
@router.get("/xtts_is_ready")
def xtts_is_ready():
if hasattr(lollmsElfServer,'sd') and lollmsElfServer.sd is not None:
if lollmsElfServer.sd.ready:
return {"status":True}
return {"status":False}

View File

@ -32,7 +32,7 @@ from lollms.utilities import git_pull, show_yes_no_dialog, run_script_in_env, cr
import subprocess
import shutil
from tqdm import tqdm
import threading
def verify_sd(lollms_paths:LollmsPaths):
# Clone repository
@ -57,24 +57,34 @@ def download_file(url, folder_path, local_filename):
return local_filename
def install_model(lollms_app:LollmsApplication, model_url):
root_dir = lollms_app.lollms_paths.personal_path
shared_folder = root_dir/"shared"
sd_folder = shared_folder / "auto_sd"
download_file(model_url, sd_folder/"models/Stable-diffusion","Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors")
def install_sd(lollms_app:LollmsApplication):
root_dir = lollms_app.lollms_paths.personal_path
shared_folder = root_dir/"shared"
sd_folder = shared_folder / "auto_sd"
if sd_folder.exists():
if show_yes_no_dialog("warning!","I have detected that there is a previous installation of stable diffusion.\nShould I remove it and continue installing?"):
shutil.rmtree(sd_folder)
elif not show_yes_no_dialog("warning!","Continue installation?"):
return
ASCIIColors.cyan("Installing autosd conda environment with python 3.10")
create_conda_env("autosd","3.10")
ASCIIColors.cyan("Done")
if os.path.exists(str(sd_folder)):
print("Repository already exists. Pulling latest changes...")
try:
subprocess.run(["git", "-C", str(sd_folder), "pull"], check=True)
subprocess.run(["git", "-C", str(sd_folder/"extensions/SD-CN-Animation"), "pull"])
except:
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
else:
print("Cloning repository...")
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")])
if show_yes_no_dialog("warning!","Do you want to install a model from civitai?\nI suggest dreamshaper xl.\nYou can install models manually by putting them inside your persona folder/auto_sd/models/stable_diffusion"):
download_file("https://civitai.com/api/download/models/351306", sd_folder/"models/Stable-diffusion","dreamshaperXL_v21TurboDPMSDE.safetensors")
if show_yes_no_dialog("warning!","Do you want to install a model?\nI suggest Juggernaut-XL_v9.\nYou can install models manually by putting them inside your persona folder/auto_sd/models/stable_diffusion"):
download_file("https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/resolve/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors", sd_folder/"models/Stable-diffusion","Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors")
lollms_app.sd = LollmsSD(lollms_app)
ASCIIColors.green("Stable diffusion installed successfully")
@ -84,15 +94,29 @@ def upgrade_sd(lollms_app:LollmsApplication):
root_dir = lollms_app.lollms_paths.personal_path
shared_folder = root_dir/"shared"
sd_folder = shared_folder / "auto_sd"
if sd_folder.exists():
if show_yes_no_dialog("warning!","I have detected that there is a previous installation of stable diffusion.\nShould I remove it and continue installing?"):
shutil.rmtree(sd_folder)
elif not show_yes_no_dialog("warning!","Continue installation?"):
return
if os.path.exists(str(sd_folder)):
print("Repository already exists. Pulling latest changes...")
try:
subprocess.run(["git", "-C", str(sd_folder), "pull"], check=True)
except:
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
try:
subprocess.run(["git", "-C", str(sd_folder/"extensions/sd-webui-deforum"), "pull"])
except:
subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")])
try:
subprocess.run(["git", "-C", str(sd_folder/"extensions/SD-CN-Animation"), "pull"])
except:
subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")])
else:
print("Cloning repository...")
subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)])
subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")])
subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")])
def upgrade_sd(lollms_app:LollmsApplication):
@ -263,6 +287,7 @@ class LollmsSD:
):
if auto_sd_base_url=="" or auto_sd_base_url=="http://127.0.0.1:7860":
auto_sd_base_url = None
self.ready = False
# Get the current directory
lollms_paths = app.lollms_paths
self.app = app
@ -322,7 +347,7 @@ class LollmsSD:
if wait_for_service:
self.wait_for_service(max_retries=max_retries)
else:
ASCIIColors.warning("We are not waiting for the SD service to be up.\nThis means that you may need to wait a bit before you can use it.")
self.wait_for_service_in_another_thread(max_retries=max_retries)
self.default_sampler = sampler
self.default_steps = steps
@ -1053,7 +1078,10 @@ class LollmsSD:
time.sleep(check_interval)
def wait_for_service_in_another_thread(self, max_retries=150, show_warning=True):
thread = threading.Thread(target=self.wait_for_service, args=(max_retries, show_warning))
thread.start()
return thread
def wait_for_service(self, max_retries = 50, show_warning=True):
url = f"{self.auto_sd_base_url}/internal/ping"
@ -1067,6 +1095,7 @@ class LollmsSD:
print("Service is available.")
if self.app is not None:
self.app.success("SD Service is now available.")
self.ready = True
return True
except requests.exceptions.RequestException:
pass

View File

@ -27,6 +27,7 @@ from dataclasses import dataclass
from PIL import Image, PngImagePlugin
from enum import Enum
from typing import List, Dict, Any
import uuid
from ascii_colors import ASCIIColors, trace_exception
from lollms.paths import LollmsPaths
@ -116,6 +117,7 @@ class LollmsXTTS:
use_deep_speed=False,
use_streaming_mode = True
):
self.generation_threads = []
self.voices_folder = voices_folder
self.ready = False
if xtts_base_url=="" or xtts_base_url=="http://127.0.0.1:8020":
@ -194,7 +196,7 @@ class LollmsXTTS:
if response.status_code == 200:
print(f"voices_folder is {self.voices_folder}.")
if self.voices_folder is not None:
print("Senerating sample audio.")
print("Generating sample audio.")
voice_file = [v for v in self.voices_folder.iterdir() if v.suffix==".wav"]
self.tts_to_audio("xtts is ready",voice_file[0].name)
print("Service is available.")
@ -260,31 +262,49 @@ class LollmsXTTS:
else:
print("Request failed with status code:", response.status_code)
def tts_to_audio(self, text, speaker_wav, file_name_or_path:Path|str=None, language="en"):
url = f"{self.xtts_base_url}/tts_to_audio"
def tts_to_audio(self, text, speaker_wav, file_name_or_path:Path|str=None, language="en", use_threading=False):
def tts2_audio_th(thread_uid=None):
url = f"{self.xtts_base_url}/tts_to_audio"
# Define the request body
payload = {
"text": text,
"speaker_wav": speaker_wav,
"language": language
}
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
# Define the request body
payload = {
"text": text,
"speaker_wav": speaker_wav,
"language": language
}
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
# Send the POST request
response = requests.post(url, headers=headers, data=json.dumps(payload))
# Send the POST request
response = requests.post(url, headers=headers, data=json.dumps(payload))
# Check the response status code
if response.status_code == 200:
print("Request successful")
# You can access the response data using response.json()
# Open a new file in binary write mode
if file_name_or_path is not None:
with open(self.output_folder/file_name_or_path, 'wb') as file:
# Write the binary content to the file
file.write(response.content)
if response.status_code == 200:
print("Request successful")
print("Response headers:", response.headers)
# Basic logging for debugging
print("First 100 bytes of response content:", response.content[:100])
if file_name_or_path is not None:
try:
with open(self.output_folder / file_name_or_path, 'wb') as file:
# Write the binary content to the file
file.write(response.content)
print(f"File {file_name_or_path} written successfully.")
except Exception as e:
print(f"Failed to write the file. Error: {e}")
else:
print("Request failed with status code:", response.status_code)
if thread_uid:
self.generation_threads.pop(thread_uid, None)
if use_threading:
thread_uid = str(uuid.uuid4())
thread = threading.Thread(target=tts2_audio_th, args=(thread_uid))
self.generation_threads[thread_uid]=thread
thread.start()
ASCIIColors.green("Generation started")
return thread
else:
print("Request failed with status code:", response.status_code)
return tts2_audio_th()

View File

@ -78,7 +78,7 @@ auto_show_browser: true
copy_to_clipboard_add_all_details: false
# Voice service
enable_voice_service: false
xtts_enable: false
xtts_base_url: http://localhost:8020
auto_read: false
xtts_current_voice: null