diff --git a/lollms/server/endpoints/lollms_binding_files_server.py b/lollms/server/endpoints/lollms_binding_files_server.py index ac03cb0..50a91fe 100644 --- a/lollms/server/endpoints/lollms_binding_files_server.py +++ b/lollms/server/endpoints/lollms_binding_files_server.py @@ -20,6 +20,7 @@ from pathlib import Path from typing import List import os +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_binding_infos.py b/lollms/server/endpoints/lollms_binding_infos.py index d85a2e9..ac07ff3 100644 --- a/lollms/server/endpoints/lollms_binding_infos.py +++ b/lollms/server/endpoints/lollms_binding_infos.py @@ -18,6 +18,7 @@ from pathlib import Path from typing import List import json +# ----------------------------------- Personal files ----------------------------------------- class ReloadBindingParams(BaseModel): binding_name: str @@ -25,6 +26,7 @@ class BindingInstallParams(BaseModel): name: str +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() @@ -74,11 +76,52 @@ def list_bindings(): return bindings # ----------------------------------- Reloading ---------------------------------------- -@router.post("/reload_binding") -def reload_binding( params: ReloadBindingParams): - print(f"Roloading binding selected : {params.binding_name}") - lollmsElfServer.config["binding_name"]=params.binding_name + +@router.post("/open_code_in_vs_code") +async def open_code_in_vs_code(request: Request): + """ + Opens code in vs code. + + :param request: The HTTP request object. + :return: A JSON response with the status of the operation. + """ + try: + data = (await request.json()) + discussion_id = data.get("discussion_id","unknown_discussion") + message_id = data.get("message_id","") + code = data["code"] + discussion_id = data.get("discussion_id","unknown_discussion") + message_id = data.get("message_id","unknown_message") + language = data.get("language","python") + + ASCIIColors.info("Opening folder:") + # Create a temporary file. + root_folder = lollmsElfServer.lollms_paths.personal_outputs_path/"discussions"/f"d_{discussion_id}"/f"{message_id}.py" + 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) + os.system('code ' + str(root_folder)) + return {"output": "OK", "execution_time": 0} + except Exception as ex: + trace_exception(ex) + lollmsElfServer.error(ex) + return {"status":False,"error":str(ex)} + +@router.post("/reload_binding") +async def reload_binding(request: Request): + """ + Opens code in vs code. + + :param request: The HTTP request object. + :return: A JSON response with the status of the operation. + """ + + try: + data = (await request.json()) + print(f"Roloading binding selected : {data['binding_name']}") + lollmsElfServer.config["binding_name"]=data['binding_name'] if lollmsElfServer.binding: lollmsElfServer.binding.destroy_model() lollmsElfServer.binding = None @@ -226,29 +269,44 @@ def get_active_binding_settings(): else: return {} -def set_active_binding_settings(data): - print("- Setting binding settings") - - if lollmsElfServer.binding is not None: - if hasattr(lollmsElfServer.binding,"binding_config"): - for entry in data: - if entry["type"]=="list" and type(entry["value"])==str: - try: - v = json.loads(entry["value"]) - except: - v= "" - if type(v)==list: - entry["value"] = v - else: - entry["value"] = [entry["value"]] - lollmsElfServer.binding.binding_config.update_template(data) - lollmsElfServer.binding.binding_config.config.save_config() - lollmsElfServer.binding.settings_updated() - if lollmsElfServer.config.auto_save: - ASCIIColors.info("Saving configuration") - lollmsElfServer.config.save_config() - return {'status':True} + +@router.post("/set_active_binding_settings") +async def set_active_binding_settings(request: Request): + """ + Opens code in vs code. + + :param request: The HTTP request object. + :return: A JSON response with the status of the operation. + """ + + try: + data = (await request.json()) + print("- Setting binding settings") + + if lollmsElfServer.binding is not None: + if hasattr(lollmsElfServer.binding,"binding_config"): + for entry in data: + if entry["type"]=="list" and type(entry["value"])==str: + try: + v = json.loads(entry["value"]) + except: + v= "" + if type(v)==list: + entry["value"] = v + else: + entry["value"] = [entry["value"]] + lollmsElfServer.binding.binding_config.update_template(data) + lollmsElfServer.binding.binding_config.config.save_config() + lollmsElfServer.binding.settings_updated() + if lollmsElfServer.config.auto_save: + ASCIIColors.info("Saving configuration") + lollmsElfServer.config.save_config() + return {'status':True} + else: + return {'status':False} else: return {'status':False} - else: - return {'status':False} + except Exception as ex: + trace_exception(ex) + lollmsElfServer.error(ex) + return {"status":False,"error":str(ex)} \ No newline at end of file diff --git a/lollms/server/endpoints/lollms_configuration_infos.py b/lollms/server/endpoints/lollms_configuration_infos.py index a4046ab..dc4b28e 100644 --- a/lollms/server/endpoints/lollms_configuration_infos.py +++ b/lollms/server/endpoints/lollms_configuration_infos.py @@ -24,6 +24,8 @@ class SettingsInfos(BaseModel): setting_name:str setting_value:str +# ----------------------- Defining router and main class ------------------------------ + router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_extensions_infos.py b/lollms/server/endpoints/lollms_extensions_infos.py index 9a399d1..db6ba7c 100644 --- a/lollms/server/endpoints/lollms_extensions_infos.py +++ b/lollms/server/endpoints/lollms_extensions_infos.py @@ -28,7 +28,8 @@ class ExtensionMountingInfos(BaseModel): folder:str language:str -# --------------------- Route ------------------------------- +# ----------------------- Defining router and main class ------------------------------ + router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_generator.py b/lollms/server/endpoints/lollms_generator.py index a490421..af27220 100644 --- a/lollms/server/endpoints/lollms_generator.py +++ b/lollms/server/endpoints/lollms_generator.py @@ -77,18 +77,20 @@ class V1InstructGenerateRequest(BaseModel): temperature: float max_tokens: float +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() elf_server = LOLLMSElfServer.get_instance() +# ----------------------------------- Generation status ----------------------------------------- @router.get("/get_generation_status") def get_generation_status(): return {"status":elf_server.busy} - +# ----------------------------------- Generation ----------------------------------------- @router.post("/generate") def lollms_generate(request_data: GenerateRequest): """ diff --git a/lollms/server/endpoints/lollms_hardware_infos.py b/lollms/server/endpoints/lollms_hardware_infos.py index d3f91b1..3c88924 100644 --- a/lollms/server/endpoints/lollms_hardware_infos.py +++ b/lollms/server/endpoints/lollms_hardware_infos.py @@ -18,6 +18,7 @@ from typing import List import psutil import subprocess +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_infos.py b/lollms/server/endpoints/lollms_infos.py index e372bd2..2b6ec45 100644 --- a/lollms/server/endpoints/lollms_infos.py +++ b/lollms/server/endpoints/lollms_infos.py @@ -16,7 +16,7 @@ from pathlib import Path from typing import List import psutil - +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_models_infos.py b/lollms/server/endpoints/lollms_models_infos.py index e4e1b0e..ed27a89 100644 --- a/lollms/server/endpoints/lollms_models_infos.py +++ b/lollms/server/endpoints/lollms_models_infos.py @@ -22,6 +22,7 @@ class ModelReferenceParams(BaseModel): path: str +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_personalities_infos.py b/lollms/server/endpoints/lollms_personalities_infos.py index 295a0c0..a95b24c 100644 --- a/lollms/server/endpoints/lollms_personalities_infos.py +++ b/lollms/server/endpoints/lollms_personalities_infos.py @@ -32,8 +32,7 @@ class PersonalityMountingInfos(BaseModel): class PersonalitySelectionInfos(BaseModel): id:int -# --------------------- Route ------------------------------- - +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/endpoints/lollms_uploads_infos.py b/lollms/server/endpoints/lollms_uploads_infos.py index d43f707..f6d680c 100644 --- a/lollms/server/endpoints/lollms_uploads_infos.py +++ b/lollms/server/endpoints/lollms_uploads_infos.py @@ -17,6 +17,7 @@ from pathlib import Path from typing import List import shutil +# ----------------------- Defining router and main class ------------------------------ router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() diff --git a/lollms/server/events/lollms_generation_events.py b/lollms/server/events/lollms_generation_events.py index 683440d..2c6373a 100644 --- a/lollms/server/events/lollms_generation_events.py +++ b/lollms/server/events/lollms_generation_events.py @@ -22,14 +22,13 @@ from typing import List import socketio import os -router = APIRouter() lollmsElfServer = LOLLMSElfServer.get_instance() # ----------------------------------- events ----------------------------------------- def add_events(sio:socketio): @sio.on('cancel_generation') - def cancel_generation(sid, environ): + def cancel_generation(sid): client_id = sid lollmsElfServer.cancel_gen = True #kill thread @@ -40,7 +39,7 @@ def add_events(sio:socketio): @sio.on('cancel_text_generation') - def cancel_text_generation(sid, environ): + def cancel_text_generation(sid): client_id = sid lollmsElfServer.connections[client_id]["requested_stop"]=True print(f"Client {client_id} requested canceling generation") @@ -51,7 +50,7 @@ def add_events(sio:socketio): # A copy of the original lollms-server generation code needed for playground @sio.on('generate_text') - def handle_generate_text(sid, environ, data): + def handle_generate_text(sid, data): client_id = sid lollmsElfServer.cancel_gen = False ASCIIColors.info(f"Text generation requested by client: {client_id}") diff --git a/lollms/server/events/lollms_model_events.py b/lollms/server/events/lollms_model_events.py new file mode 100644 index 0000000..e67d351 --- /dev/null +++ b/lollms/server/events/lollms_model_events.py @@ -0,0 +1,311 @@ +""" +project: lollms +file: lollms_binding_files_server.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 are specific to serving files + +""" +from fastapi import APIRouter, Request +from fastapi import HTTPException +from pydantic import BaseModel +import pkg_resources +from lollms.server.elf_server import LOLLMSElfServer +from fastapi.responses import FileResponse +from lollms.binding import BindingBuilder, InstallOption +from ascii_colors import ASCIIColors +from lollms.personality import MSG_TYPE, AIPersonality +from lollms.utilities import load_config, trace_exception, gc, terminate_thread, run_async +from pathlib import Path +from typing import List +import socketio +from datetime import datetime +from functools import partial +import shutil +import threading +import os + +lollmsElfServer = LOLLMSElfServer.get_instance() + + +# ----------------------------------- events ----------------------------------------- +def add_events(sio:socketio): + @sio.on('install_model') + def install_model(sid, data): + room_id = sid + def install_model_(): + print("Install model triggered") + model_path = data["path"].replace("\\","/") + + if data["type"].lower() in model_path.lower(): + model_type:str=data["type"] + else: + mtt = None + for mt in lollmsElfServer.binding.models_dir_names: + if mt.lower() in model_path.lower(): + mtt = mt + break + if mtt: + model_type = mtt + else: + model_type:str=lollmsElfServer.binding.models_dir_names[0] + + progress = 0 + installation_dir = lollmsElfServer.binding.searchModelParentFolder(model_path.split('/')[-1], model_type) + if model_type=="gptq" or model_type=="awq" or model_type=="transformers": + parts = model_path.split("/") + if len(parts)==2: + filename = parts[1] + else: + filename = parts[4] + installation_path = installation_dir / filename + elif model_type=="gpt4all": + filename = data["variant_name"] + model_path = "http://gpt4all.io/models/gguf/"+filename + installation_path = installation_dir / filename + else: + filename = Path(model_path).name + installation_path = installation_dir / filename + print("Model install requested") + print(f"Model path : {model_path}") + + model_name = filename + binding_folder = lollmsElfServer.config["binding_name"] + model_url = model_path + signature = f"{model_name}_{binding_folder}_{model_url}" + try: + lollmsElfServer.download_infos[signature]={ + "start_time":datetime.now(), + "total_size":lollmsElfServer.binding.get_file_size(model_path), + "downloaded_size":0, + "progress":0, + "speed":0, + "cancel":False + } + + if installation_path.exists(): + print("Error: Model already exists. please remove it first") + run_async( partial(sio.emit,'install_progress',{ + 'status': False, + 'error': f'model already exists. Please remove it first.\nThe model can be found here:{installation_path}', + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': lollmsElfServer.download_infos[signature]['progress'], + 'speed': lollmsElfServer.download_infos[signature]['speed'], + }, room=room_id + ) + ) + return + + run_async( partial(sio.emit,'install_progress',{ + 'status': True, + 'progress': progress, + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': lollmsElfServer.download_infos[signature]['progress'], + 'speed': lollmsElfServer.download_infos[signature]['speed'], + + }, room=room_id) + ) + + def callback(downloaded_size, total_size): + progress = (downloaded_size / total_size) * 100 + now = datetime.now() + dt = (now - lollmsElfServer.download_infos[signature]['start_time']).total_seconds() + speed = downloaded_size/dt + lollmsElfServer.download_infos[signature]['downloaded_size'] = downloaded_size + lollmsElfServer.download_infos[signature]['speed'] = speed + + if progress - lollmsElfServer.download_infos[signature]['progress']>2: + lollmsElfServer.download_infos[signature]['progress'] = progress + run_async( partial(sio.emit,'install_progress',{ + 'status': True, + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': lollmsElfServer.download_infos[signature]['progress'], + 'speed': lollmsElfServer.download_infos[signature]['speed'], + }, room=room_id) + ) + + if lollmsElfServer.download_infos[signature]["cancel"]: + raise Exception("canceled") + + + if hasattr(lollmsElfServer.binding, "download_model"): + try: + lollmsElfServer.binding.download_model(model_path, installation_path, callback) + except Exception as ex: + ASCIIColors.warning(str(ex)) + trace_exception(ex) + run_async( partial(sio.emit,'install_progress',{ + 'status': False, + 'error': 'canceled', + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': lollmsElfServer.download_infos[signature]['progress'], + 'speed': lollmsElfServer.download_infos[signature]['speed'], + }, room=room_id + ) + ) + del lollmsElfServer.download_infos[signature] + try: + if installation_path.is_dir(): + shutil.rmtree(installation_path) + else: + installation_path.unlink() + except Exception as ex: + trace_exception(ex) + ASCIIColors.error(f"Couldn't delete file. Please try to remove it manually.\n{installation_path}") + return + + else: + try: + lollmsElfServer.download_file(model_path, installation_path, callback) + except Exception as ex: + ASCIIColors.warning(str(ex)) + trace_exception(ex) + run_async( partial(sio.emit,'install_progress',{ + 'status': False, + 'error': 'canceled', + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': lollmsElfServer.download_infos[signature]['progress'], + 'speed': lollmsElfServer.download_infos[signature]['speed'], + }, room=room_id + ) + ) + del lollmsElfServer.download_infos[signature] + installation_path.unlink() + return + run_async( partial(sio.emit,'install_progress',{ + 'status': True, + 'error': '', + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': lollmsElfServer.download_infos[signature]['start_time'].strftime("%Y-%m-%d %H:%M:%S"), + 'total_size': lollmsElfServer.download_infos[signature]['total_size'], + 'downloaded_size': lollmsElfServer.download_infos[signature]['downloaded_size'], + 'progress': 100, + 'speed': lollmsElfServer.download_infos[signature]['speed'], + }, room=room_id) + ) + del lollmsElfServer.download_infos[signature] + except Exception as ex: + trace_exception(ex) + run_async( partial(sio.emit,'install_progress',{ + 'status': False, + 'error': str(ex), + 'model_name' : model_name, + 'binding_folder' : binding_folder, + 'model_url' : model_url, + 'start_time': '', + 'total_size': 0, + 'downloaded_size': 0, + 'progress': 0, + 'speed': 0, + }, room=room_id + ) + ) + tpe = threading.Thread(target=install_model_, args=()) + tpe.start() + + @sio.on('uninstall_model') + def uninstall_model(sid, data): + model_path = data['path'] + model_type:str=data.get("type","ggml") + installation_dir = lollmsElfServer.binding.searchModelParentFolder(model_path) + + binding_folder = lollmsElfServer.config["binding_name"] + if model_type=="gptq" or model_type=="awq": + filename = model_path.split("/")[4] + installation_path = installation_dir / filename + else: + filename = Path(model_path).name + installation_path = installation_dir / filename + model_name = filename + + if not installation_path.exists(): + run_async( partial(sio.emit,'uninstall_progress',{ + 'status': False, + 'error': 'The model does not exist', + 'model_name' : model_name, + 'binding_folder' : binding_folder + }, room=sid) + ) + try: + if not installation_path.exists(): + # Try to find a version + model_path = installation_path.name.lower().replace("-ggml","").replace("-gguf","") + candidates = [m for m in installation_dir.iterdir() if model_path in m.name] + if len(candidates)>0: + model_path = candidates[0] + installation_path = model_path + + if installation_path.is_dir(): + shutil.rmtree(installation_path) + else: + installation_path.unlink() + run_async( partial(sio.emit,'uninstall_progress',{ + 'status': True, + 'error': '', + 'model_name' : model_name, + 'binding_folder' : binding_folder + }, room=sid) + ) + except Exception as ex: + trace_exception(ex) + ASCIIColors.error(f"Couldn't delete {installation_path}, please delete it manually and restart the app") + run_async( partial(sio.emit,'uninstall_progress',{ + 'status': False, + 'error': f"Couldn't delete {installation_path}, please delete it manually and restart the app", + 'model_name' : model_name, + 'binding_folder' : binding_folder + }, room=sid) + ) + + + @sio.on('cancel_install') + def cancel_install(sid,data): + try: + model_name = data["model_name"] + binding_folder = data["binding_folder"] + model_url = data["model_url"] + signature = f"{model_name}_{binding_folder}_{model_url}" + lollmsElfServer.download_infos[signature]["cancel"]=True + + run_async( partial(sio.emit,'canceled', { + 'status': True + }, + room=sid + ) + ) + except Exception as ex: + trace_exception(ex) + sio.emit('canceled', { + 'status': False, + 'error':str(ex) + }, + room=sid + ) diff --git a/lollms/utilities.py b/lollms/utilities.py index ee429f9..3bbb106 100644 --- a/lollms/utilities.py +++ b/lollms/utilities.py @@ -92,6 +92,28 @@ def convert_language_name(language_name): return language_codes.get(language_name,"en") +# Function to encode the image +def encode_image(image_path, max_image_width=-1): + image = Image.open(image_path) + width, height = image.size + + if max_image_width != -1 and width > max_image_width: + ratio = max_image_width / width + new_width = max_image_width + new_height = int(height * ratio) + image = image.resize((new_width, new_height)) + + # Check and convert image format if needed + if image.format not in ['PNG', 'JPEG', 'GIF', 'WEBP']: + image = image.convert('JPEG') + + # Save the image to a BytesIO object + byte_arr = io.BytesIO() + image.save(byte_arr, format=image.format) + byte_arr = byte_arr.getvalue() + + return base64.b64encode(byte_arr).decode('utf-8') + def load_config(file_path): with open(file_path, 'r', encoding='utf-8') as stream: config = yaml.safe_load(stream)