lollms-webui/app.py

438 lines
18 KiB
Python
Raw Normal View History

2024-01-13 09:30:27 +00:00
"""
File: lollms_web_ui.py
Author: ParisNeo
Description: Singleton class for the LoLLMS web UI.
This file is the entry point to the webui.
"""
from lollms.utilities import PackageManager
2024-07-25 13:25:28 +00:00
from fastapi.middleware.cors import CORSMiddleware
2024-08-11 18:11:14 +00:00
import threading
import time
import sys
from typing import List, Tuple
2024-09-15 14:30:06 +00:00
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
2024-09-16 17:09:31 +00:00
2024-08-11 18:11:14 +00:00
expected_ascii_colors_version = "0.4.2"
2024-07-22 16:44:58 +00:00
print(f"Checking ascii_colors ({expected_ascii_colors_version}) ...", end="", flush=True)
2024-08-11 18:11:14 +00:00
if not PackageManager.check_package_installed_with_version("ascii_colors", expected_ascii_colors_version):
2024-07-22 16:44:58 +00:00
PackageManager.install_or_update("ascii_colors")
from ascii_colors import ASCIIColors
ASCIIColors.success("OK")
2024-10-09 11:54:39 +00:00
expected_pipmaster_version = "0.3.2"
2024-10-09 08:39:06 +00:00
ASCIIColors.yellow(f"Checking pipmaster ({expected_pipmaster_version}) ...", end="", flush=True)
if not PackageManager.check_package_installed_with_version("pipmaster", expected_pipmaster_version):
PackageManager.install_or_update("pipmaster")
2024-08-11 18:11:14 +00:00
import pipmaster as pm
2024-07-22 16:44:58 +00:00
ASCIIColors.success("OK")
2024-08-11 18:11:14 +00:00
def animate(text: str, stop_event: threading.Event):
animation = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
idx = 0
while not stop_event.is_set():
ASCIIColors.yellow(f"\r{text} {animation[idx % len(animation)]}", end="", flush=True)
idx += 1
time.sleep(0.1)
print("\r" + " " * 50, end="\r") # Clear the line
2024-08-11 18:11:14 +00:00
def check_and_install_package(package: str, version: str):
stop_event = threading.Event()
animation_thread = threading.Thread(target=animate, args=(f"Checking {package} ({version})", stop_event))
animation_thread.start()
try:
installed = PackageManager.check_package_installed_with_version(package, version)
if not installed:
stop_event.set()
animation_thread.join()
print("\r" + " " * 50, end="\r") # Clear the line
PackageManager.install_or_update(package)
stop_event.set()
animation_thread.join()
print("\r" + " " * 50, end="\r") # Clear the line
ASCIIColors.yellow(f"Checking {package} ({version}) ...", end="")
ASCIIColors.success("OK")
except Exception as e:
stop_event.set()
animation_thread.join()
print("\r" + " " * 50, end="\r") # Clear the line
ASCIIColors.red(f"Error checking/installing {package}: {str(e)}")
packages: List[Tuple[str, str]] = [
2024-08-29 10:23:39 +00:00
("freedom_search", "0.1.9"),
2024-10-07 21:20:14 +00:00
("scrapemaster", "0.2.1"),
2024-11-20 22:24:31 +00:00
("lollms_client", "0.7.7"),
2024-12-06 07:48:49 +00:00
("lollmsvectordb", "1.2.4"),
2024-08-11 18:11:14 +00:00
]
2024-09-16 17:09:31 +00:00
def check_pn_libs():
2024-08-11 18:11:14 +00:00
ASCIIColors.cyan("Checking ParisNeo libraries installation")
print()
2024-07-22 19:18:19 +00:00
2024-08-11 18:11:14 +00:00
for package, version in packages:
check_and_install_package(package, version)
print() # Add a newline for better readability between package checks
2024-08-11 18:11:14 +00:00
ASCIIColors.green("All packages have been checked and are up to date!")
2024-01-13 09:30:27 +00:00
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
2024-06-06 21:19:03 +00:00
from starlette.responses import FileResponse
2024-01-13 09:30:27 +00:00
from lollms.paths import LollmsPaths
from lollms.main_config import LOLLMSConfig
from lollms.utilities import trace_exception
2024-09-12 07:13:51 +00:00
from lollms.security import sanitize_path
2024-01-13 09:30:27 +00:00
from lollms_webui import LOLLMSWebUI
from pathlib import Path
from ascii_colors import ASCIIColors
import socketio
import uvicorn
import argparse
from socketio import ASGIApp
import webbrowser
2024-02-14 23:35:04 +00:00
import os
2024-03-01 00:34:54 +00:00
import sys
2024-01-27 21:02:31 +00:00
2024-02-17 23:14:52 +00:00
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
2024-03-20 09:02:20 +00:00
from pydantic import ValidationError
2024-02-17 23:14:52 +00:00
from fastapi.encoders import jsonable_encoder
2024-03-31 01:01:12 +00:00
from fastapi.middleware.cors import CORSMiddleware
import socket
import psutil
def get_ip_addresses():
hostname = socket.gethostname()
ip_addresses = [socket.gethostbyname(hostname)]
for interface_name, interface_addresses in psutil.net_if_addrs().items():
for address in interface_addresses:
if str(address.family) == 'AddressFamily.AF_INET':
ip_addresses.append(address.address)
return ip_addresses
2024-02-17 23:14:52 +00:00
2024-02-16 21:44:44 +00:00
app = FastAPI(title="LoLLMS", description="This is the LoLLMS-Webui API documentation")
2023-04-20 17:30:03 +00:00
2023-12-26 01:46:50 +00:00
2024-09-12 07:13:51 +00:00
try:
from lollms.security import MultipartBoundaryCheck
# Add the MultipartBoundaryCheck middleware
app.add_middleware(MultipartBoundaryCheck)
except:
print("Couldn't activate MultipartBoundaryCheck")
2023-12-26 01:46:50 +00:00
2024-01-13 09:30:27 +00:00
#app.mount("/socket.io", StaticFiles(directory="path/to/socketio.js"))
2023-05-28 06:47:57 +00:00
2024-01-13 09:30:27 +00:00
if __name__ == "__main__":
2024-03-01 00:34:54 +00:00
desired_version = (3, 11)
if not sys.version_info >= desired_version:
ASCIIColors.error(f"Your Python version is {sys.version_info.major}.{sys.version_info.minor}, but version {desired_version[0]}.{desired_version[1]} or higher is required.")
sys.exit(1)
2024-01-13 09:30:27 +00:00
# Parsong parameters
parser = argparse.ArgumentParser(description="Start the chatbot FastAPI app.")
2023-07-26 16:12:24 +00:00
2024-01-13 09:30:27 +00:00
parser.add_argument(
"--host", type=str, default=None, help="the hostname to listen on"
2023-07-26 16:12:24 +00:00
)
2024-01-13 09:30:27 +00:00
parser.add_argument("--port", type=int, default=None, help="the port to listen on")
args = parser.parse_args()
root_path = Path(__file__).parent
lollms_paths = LollmsPaths.find_paths(force_local=True, custom_default_cfg_path="configs/config.yaml")
config = LOLLMSConfig.autoload(lollms_paths)
2024-01-27 21:02:31 +00:00
2024-09-16 17:09:31 +00:00
if config.auto_update:
check_pn_libs()
2024-01-27 21:02:31 +00:00
if config.debug_log_file_path!="":
ASCIIColors.log_path = config.debug_log_file_path
2024-01-13 09:30:27 +00:00
if args.host:
config.host=args.host
if args.port:
config.port=args.port
2024-06-06 07:01:33 +00:00
# Define the path to your custom CA bundle file
ca_bundle_path = lollms_paths.personal_certificates/"truststore.pem"
if ca_bundle_path.exists():
# Set the environment variable
os.environ['REQUESTS_CA_BUNDLE'] = str(ca_bundle_path)
2024-02-14 23:35:04 +00:00
cert_file_path = lollms_paths.personal_certificates/"cert.pem"
key_file_path = lollms_paths.personal_certificates/"key.pem"
if os.path.exists(cert_file_path) and os.path.exists(key_file_path):
is_https = True
else:
is_https = False
# Create a Socket.IO server
if config["host"]!="localhost":
if config["host"]!="0.0.0.0":
2024-02-29 12:57:58 +00:00
config.allowed_origins.append(f"https://{config['host']}:{config['port']}" if is_https else f"http://{config['host']}:{config['port']}")
else:
2024-02-29 12:57:58 +00:00
config.allowed_origins += [f"https://{ip}:{config['port']}" if is_https else f"http://{ip}:{config['port']}" for ip in get_ip_addresses()]
2024-02-26 18:43:33 +00:00
allowed_origins = config.allowed_origins+[f"https://localhost:{config['port']}" if is_https else f"http://localhost:{config['port']}"]
2024-03-31 01:01:12 +00:00
2024-07-25 13:25:28 +00:00
# class EndpointSpecificCORSMiddleware(BaseHTTPMiddleware):
# async def dispatch(self, request: Request, call_next):
# if request.url.path == "/v1/completions":
# # For /v1/completions, allow all origins
# response = await call_next(request)
# response.headers["Access-Control-Allow-Origin"] = "*"
# response.headers["Access-Control-Allow-Methods"] = "*"
# response.headers["Access-Control-Allow-Headers"] = "*"
# return response
# else:
# # For other endpoints, use the restricted CORS policy
# origin = request.headers.get("origin")
# if origin in allowed_origins:
# response = await call_next(request)
# response.headers["Access-Control-Allow-Origin"] = origin
# response.headers["Access-Control-Allow-Credentials"] = "true"
# response.headers["Access-Control-Allow-Methods"] = "*"
# response.headers["Access-Control-Allow-Headers"] = "*"
# return response
# else:
# return await call_next(request)
# # Add the custom middleware
# app.add_middleware(EndpointSpecificCORSMiddleware)
2024-03-31 01:01:12 +00:00
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2024-07-25 13:25:28 +00:00
2024-02-26 18:43:33 +00:00
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins=allowed_origins, ping_timeout=1200, ping_interval=30) # Enable CORS for selected origins
2024-02-14 23:35:04 +00:00
# A simple fix for v 11.0 to 12 alpha
if config.rag_vectorizer=="bert":
2024-09-25 22:46:31 +00:00
config.rag_vectorizer="tfidf"
config.save_config()
2024-01-16 23:06:38 +00:00
LOLLMSWebUI.build_instance(config=config, lollms_paths=lollms_paths, args=args, sio=sio)
2024-01-13 09:30:27 +00:00
lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance()
2024-01-24 19:34:23 +00:00
lollmsElfServer.verbose = True
2024-02-14 23:35:04 +00:00
2024-01-13 09:30:27 +00:00
# Import all endpoints
from lollms.server.endpoints.lollms_binding_files_server import router as lollms_binding_files_server_router
from lollms.server.endpoints.lollms_infos import router as lollms_infos_router
from lollms.server.endpoints.lollms_hardware_infos import router as lollms_hardware_infos_router
from lollms.server.endpoints.lollms_binding_infos import router as lollms_binding_infos_router
from lollms.server.endpoints.lollms_models_infos import router as lollms_models_infos_router
from lollms.server.endpoints.lollms_personalities_infos import router as lollms_personalities_infos_router
from lollms.server.endpoints.lollms_generator import router as lollms_generator_router
from lollms.server.endpoints.lollms_configuration_infos import router as lollms_configuration_infos_router
2024-02-27 16:06:22 +00:00
from lollms.server.endpoints.lollms_skills_library import router as lollms_skills_library_router
2024-01-13 09:30:27 +00:00
2024-08-25 22:46:26 +00:00
from lollms.server.endpoints.lollms_tti import router as lollms_tti_router
2024-06-25 21:15:43 +00:00
2024-01-29 23:09:04 +00:00
from lollms.server.endpoints.lollms_user import router as lollms_user_router
2024-09-14 23:37:41 +00:00
from lollms.server.endpoints.lollms_tts import router as lollms_tts_add_router
from lollms.server.endpoints.lollms_xtts import router as lollms_xtts_add_router
from lollms.server.endpoints.lollms_whisper import router as lollms_whisper
2024-01-29 23:09:04 +00:00
from lollms.server.endpoints.lollms_sd import router as lollms_sd_router
2024-05-27 22:00:00 +00:00
from lollms.server.endpoints.lollms_diffusers import router as lollms_diffusers_router
2024-03-17 22:36:40 +00:00
from lollms.server.endpoints.lollms_comfyui import router as lollms_comfyui_router
2024-01-29 23:09:04 +00:00
from lollms.server.endpoints.lollms_ollama import router as lollms_ollama_router
from lollms.server.endpoints.lollms_vllm import router as lollms_vllm_router
2024-02-27 16:06:22 +00:00
from lollms.server.endpoints.lollms_motion_ctrl import router as lollms_motion_ctrl_router
2024-06-25 21:15:43 +00:00
from lollms.server.endpoints.lollms_discussion import router as lollms_discussion_router
2024-08-06 09:47:49 +00:00
from lollms.server.endpoints.lollms_petals import router as lollms_petals_router
from lollms.server.endpoints.lollms_rag import router as lollms_rag_router
2024-06-25 21:15:43 +00:00
2024-01-29 23:09:04 +00:00
2024-01-13 09:30:27 +00:00
from endpoints.lollms_webui_infos import router as lollms_webui_infos_router
from endpoints.lollms_message import router as lollms_message_router
from endpoints.lollms_advanced import router as lollms_advanced_router
2024-07-27 23:19:18 +00:00
from endpoints.lollms_apps import router as lollms_apps_router
2024-01-13 09:30:27 +00:00
from endpoints.chat_bar import router as chat_bar_router
2024-06-25 21:15:43 +00:00
from endpoints.lollms_help import router as help_router
2024-01-25 00:41:28 +00:00
2024-01-13 09:30:27 +00:00
from endpoints.lollms_playground import router as lollms_playground_router
2023-07-26 16:12:24 +00:00
2024-06-16 23:49:44 +00:00
from lollms.server.endpoints.lollms_file_system import router as lollms_file_system_router
2023-11-08 14:37:05 +00:00
2024-01-13 09:30:27 +00:00
from lollms.server.events.lollms_generation_events import add_events as lollms_generation_events_add
from lollms.server.events.lollms_personality_events import add_events as lollms_personality_events_add
from lollms.server.events.lollms_files_events import add_events as lollms_files_events_add
2024-01-14 00:05:54 +00:00
from lollms.server.events.lollms_model_events import add_events as lollms_model_events_add
2024-03-19 06:48:03 +00:00
#from lollms.server.events.lollms_rag_events import add_events as lollms_rag_events_add
2024-02-27 16:06:22 +00:00
2024-01-14 00:05:54 +00:00
2024-01-13 09:30:27 +00:00
from events.lollms_generation_events import add_events as lollms_webui_generation_events_add
from events.lollms_discussion_events import add_events as lollms_webui_discussion_events_add
from events.lollms_chatbox_events import add_events as lollms_chatbox_events_add
from events.lollms_interactive_events import add_events as lollms_interactive_events_add
2023-11-08 14:37:05 +00:00
2023-07-17 11:03:42 +00:00
2024-02-17 23:14:52 +00:00
# endpoints for remote access
2024-01-13 09:30:27 +00:00
app.include_router(lollms_generator_router)
2023-07-17 11:03:42 +00:00
2024-02-17 23:14:52 +00:00
# Endpoints reserved for local access
2024-02-23 20:20:37 +00:00
if (not config.headless_server_mode) or config.force_accept_remote_access: # Be aware that forcing force_accept_remote_access can expose the server to attacks
2024-02-17 23:14:52 +00:00
app.include_router(lollms_infos_router)
app.include_router(lollms_binding_files_server_router)
app.include_router(lollms_hardware_infos_router)
app.include_router(lollms_binding_infos_router)
app.include_router(lollms_models_infos_router)
app.include_router(lollms_personalities_infos_router)
2024-02-27 16:06:22 +00:00
app.include_router(lollms_skills_library_router)
2024-08-25 22:46:26 +00:00
app.include_router(lollms_tti_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_webui_infos_router)
app.include_router(lollms_discussion_router)
app.include_router(lollms_message_router)
app.include_router(lollms_user_router)
app.include_router(lollms_advanced_router)
2024-07-27 23:19:18 +00:00
app.include_router(lollms_apps_router)
2024-02-17 23:14:52 +00:00
app.include_router(chat_bar_router)
2024-06-25 21:15:43 +00:00
app.include_router(help_router)
2024-09-14 23:37:41 +00:00
app.include_router(lollms_tts_add_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_xtts_add_router)
2024-09-14 23:37:41 +00:00
app.include_router(lollms_whisper)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_sd_router)
2024-05-27 22:00:00 +00:00
app.include_router(lollms_diffusers_router)
2024-03-17 22:36:40 +00:00
app.include_router(lollms_comfyui_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_ollama_router)
app.include_router(lollms_petals_router)
2024-08-06 09:47:49 +00:00
app.include_router(lollms_rag_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_vllm_router)
2024-02-27 16:06:22 +00:00
app.include_router(lollms_motion_ctrl_router)
2024-02-19 21:40:28 +00:00
2024-06-16 23:49:44 +00:00
app.include_router(lollms_file_system_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_playground_router)
app.include_router(lollms_configuration_infos_router)
2024-02-14 23:35:04 +00:00
@sio.event
async def disconnect(sid):
ASCIIColors.yellow(f"Disconnected: {sid}")
@sio.event
async def message(sid, data):
ASCIIColors.yellow(f"Message from {sid}: {data}")
await sio.send(sid, "Message received!")
2023-07-17 11:03:42 +00:00
2024-01-13 09:30:27 +00:00
lollms_generation_events_add(sio)
2024-03-23 22:50:33 +00:00
if (not config.headless_server_mode) or config.force_accept_remote_access: # Be aware that forcing force_accept_remote_access can expose the server to attacks
lollms_personality_events_add(sio)
lollms_files_events_add(sio)
lollms_model_events_add(sio)
#lollms_rag_events_add(sio)
lollms_webui_generation_events_add(sio)
lollms_webui_discussion_events_add(sio)
lollms_chatbox_events_add(sio)
lollms_interactive_events_add(sio)
2023-06-10 13:49:41 +00:00
2023-11-08 14:37:05 +00:00
2024-01-13 09:30:27 +00:00
app.mount("/extensions", StaticFiles(directory=Path(__file__).parent/"web"/"dist", html=True), name="extensions")
app.mount("/playground", StaticFiles(directory=Path(__file__).parent/"web"/"dist", html=True), name="playground")
app.mount("/settings", StaticFiles(directory=Path(__file__).parent/"web"/"dist", html=True), name="settings")
2024-06-06 21:19:03 +00:00
# Custom route to serve JavaScript files with the correct MIME type
@app.get("/{path:path}")
async def serve_js(path: str):
2024-06-27 21:05:51 +00:00
sanitize_path(path)
2024-06-06 21:19:03 +00:00
if path=="":
return FileResponse(Path(__file__).parent / "web" / "dist" / "index.html", media_type="text/html")
file_path = Path(__file__).parent / "web" / "dist" / path
if file_path.suffix == ".js":
return FileResponse(file_path, media_type="application/javascript")
return FileResponse(file_path)
2024-01-13 09:30:27 +00:00
app.mount("/", StaticFiles(directory=Path(__file__).parent/"web"/"dist", html=True), name="static")
2024-02-14 23:35:04 +00:00
2024-02-17 23:14:52 +00:00
@app.exception_handler(ValidationError)
async def validation_exception_handler(request: Request, exc: ValidationError):
print(f"Error: {exc.errors()}") # Print the validation error details
return JSONResponse(
status_code=422,
content=jsonable_encoder({"detail": exc.errors(), "body": await exc.body}), # Send the error details and the original request body
)
2024-02-14 23:35:04 +00:00
2024-01-13 09:30:27 +00:00
app = ASGIApp(socketio_server=sio, other_asgi_app=app)
2023-11-08 14:37:05 +00:00
2024-02-17 23:14:52 +00:00
2024-01-13 09:30:27 +00:00
lollmsElfServer.app = app
2023-11-08 14:37:05 +00:00
2024-01-13 09:30:27 +00:00
try:
sio.reboot = False
2024-01-21 04:35:29 +00:00
# if config.enable_lollms_service:
# ASCIIColors.yellow("Starting Lollms service")
# #uvicorn.run(app, host=config.host, port=6523)
# def run_lollms_server():
# parts = config.lollms_base_url.split(":")
# host = ":".join(parts[0:2])
# port = int(parts[2])
# uvicorn.run(app, host=host, port=port)
2024-01-13 23:37:43 +00:00
# New thread
2024-01-21 04:35:29 +00:00
# thread = threading.Thread(target=run_lollms_server)
2024-01-13 23:37:43 +00:00
# start thread
2024-01-21 04:35:29 +00:00
# thread.start()
# if autoshow
2024-02-14 23:35:04 +00:00
if config.auto_show_browser and not config.headless_server_mode:
2024-01-21 04:35:29 +00:00
if config['host']=="0.0.0.0":
2024-02-14 23:35:04 +00:00
webbrowser.open(f"https://localhost:{config['port']}" if is_https else f"http://localhost:{config['port']}")
2024-01-21 04:35:29 +00:00
#webbrowser.open(f"http://localhost:{6523}") # needed for debug (to be removed in production)
else:
2024-02-14 23:35:04 +00:00
webbrowser.open(f"https://{config['host']}:{config['port']}" if is_https else f"http://{config['host']}:{config['port']}")
2024-01-21 04:35:29 +00:00
#webbrowser.open(f"http://{config['host']}:{6523}") # needed for debug (to be removed in production)
2024-02-14 23:35:04 +00:00
if is_https:
uvicorn.run(app, host=config.host, port=config.port, ssl_certfile=cert_file_path, ssl_keyfile=key_file_path)
else:
uvicorn.run(app, host=config.host, port=config.port)
2024-01-24 11:08:07 +00:00
2024-01-13 09:30:27 +00:00
except Exception as ex:
trace_exception(ex)
2023-11-09 01:00:49 +00:00
2024-01-19 13:34:44 +00:00