lollms-webui/app.py

534 lines
19 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.
"""
2024-12-19 12:48:57 +00:00
import os
import sys
2024-08-11 18:11:14 +00:00
import threading
import time
from typing import List, Tuple
2024-12-19 12:48:57 +00:00
from fastapi.middleware.cors import CORSMiddleware
from lollms.utilities import PackageManager
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-12-19 12:48:57 +00:00
print(
f"Checking ascii_colors ({expected_ascii_colors_version}) ...", end="", flush=True
)
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
2024-12-19 12:48:57 +00:00
2024-07-22 16:44:58 +00:00
ASCIIColors.success("OK")
2024-10-09 11:54:39 +00:00
expected_pipmaster_version = "0.3.2"
2024-12-19 12:48:57 +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-12-19 12:48:57 +00:00
ASCIIColors.success("OK")
2024-12-15 23:47:58 +00:00
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():
2024-12-19 12:48:57 +00:00
ASCIIColors.yellow(
f"\r{text} {animation[idx % len(animation)]}", end="", flush=True
)
2024-08-11 18:11:14 +00:00
idx += 1
time.sleep(0.1)
print("\r" + " " * 50, end="\r") # Clear the line
2024-12-19 12:48:57 +00:00
2024-08-11 18:11:14 +00:00
def check_and_install_package(package: str, version: str):
stop_event = threading.Event()
2024-12-19 12:48:57 +00:00
animation_thread = threading.Thread(
target=animate, args=(f"Checking {package} ({version})", stop_event)
)
2024-08-11 18:11:14 +00:00
animation_thread.start()
try:
2024-12-19 12:48:57 +00:00
installed = PackageManager.check_package_installed_with_version(
package, version
)
2024-08-11 18:11:14 +00:00
if not installed:
stop_event.set()
animation_thread.join()
print("\r" + " " * 50, end="\r") # Clear the line
PackageManager.install_or_update(package)
2024-12-19 12:48:57 +00:00
2024-08-11 18:11:14 +00:00
stop_event.set()
animation_thread.join()
2024-12-19 12:48:57 +00:00
2024-08-11 18:11:14 +00:00
print("\r" + " " * 50, end="\r") # Clear the line
ASCIIColors.yellow(f"Checking {package} ({version}) ...", end="")
ASCIIColors.success("OK")
2024-12-19 12:48:57 +00:00
2024-08-11 18:11:14 +00:00
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)}")
2024-12-19 12:48:57 +00:00
2024-08-11 18:11:14 +00:00
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-15 23:47:58 +00:00
("lollmsvectordb", "1.2.8"),
2024-08-11 18:11:14 +00:00
]
2024-12-19 12:48:57 +00:00
if not pm.is_installed("einops"):
2024-12-15 23:47:58 +00:00
pm.install("einops")
2024-12-19 12:48:57 +00:00
if not pm.is_installed("datasets"):
2024-12-15 23:47:58 +00:00
pm.install("datasets")
2024-12-19 12:48:57 +00:00
# einops datasets
2024-12-15 23:47:58 +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-12-19 12:48:57 +00:00
2024-01-13 09:30:27 +00:00
import argparse
2024-02-14 23:35:04 +00:00
import os
2024-12-19 12:48:57 +00:00
import socket
2024-03-01 00:34:54 +00:00
import sys
2024-12-19 12:48:57 +00:00
import webbrowser
from pathlib import Path
2024-03-01 00:34:54 +00:00
2024-12-19 12:48:57 +00:00
import psutil
import socketio
import uvicorn
from ascii_colors import ASCIIColors
2024-02-17 23:14:52 +00:00
from fastapi import FastAPI, Request
from fastapi.encoders import jsonable_encoder
2024-03-31 01:01:12 +00:00
from fastapi.middleware.cors import CORSMiddleware
2024-12-19 12:48:57 +00:00
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from lollms.main_config import LOLLMSConfig
from lollms.paths import LollmsPaths
from lollms.security import sanitize_path
from lollms.utilities import trace_exception
from pydantic import ValidationError
from socketio import ASGIApp
from starlette.responses import FileResponse
from lollms_webui import LOLLMSWebUI
def get_ip_addresses():
2024-12-19 12:48:57 +00:00
hostname = socket.gethostname()
ip_addresses = [socket.gethostbyname(hostname)]
for interface_name, interface_addresses in psutil.net_if_addrs().items():
for address in interface_addresses:
2024-12-19 12:48:57 +00:00
if str(address.family) == "AddressFamily.AF_INET":
ip_addresses.append(address.address)
return ip_addresses
2024-02-17 23:14:52 +00:00
2024-12-19 12:48:57 +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
2024-12-19 12:48:57 +00:00
2024-09-12 07:13:51 +00:00
# Add the MultipartBoundaryCheck middleware
app.add_middleware(MultipartBoundaryCheck)
except:
print("Couldn't activate MultipartBoundaryCheck")
2023-12-26 01:46:50 +00:00
2024-12-19 12:48:57 +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:
2024-12-19 12:48:57 +00:00
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."
)
2024-03-01 00:34:54 +00:00
sys.exit(1)
2024-01-13 09:30:27 +00:00
# Parsong parameters
parser = argparse.ArgumentParser(description="Start the chatbot FastAPI app.")
2024-12-19 12:48:57 +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
2024-12-19 12:48:57 +00:00
lollms_paths = LollmsPaths.find_paths(
force_local=True, custom_default_cfg_path="configs/config.yaml"
)
2024-01-13 09:30:27 +00:00
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-12-19 12:48:57 +00:00
if config.debug_log_file_path != "":
2024-01-27 21:02:31 +00:00
ASCIIColors.log_path = config.debug_log_file_path
2024-01-13 09:30:27 +00:00
if args.host:
2024-12-19 12:48:57 +00:00
config.host = args.host
2024-01-13 09:30:27 +00:00
if args.port:
2024-12-19 12:48:57 +00:00
config.port = args.port
2024-01-13 09:30:27 +00:00
2024-06-06 07:01:33 +00:00
# Define the path to your custom CA bundle file
2024-12-19 12:48:57 +00:00
ca_bundle_path = lollms_paths.personal_certificates / "truststore.pem"
2024-06-06 07:01:33 +00:00
if ca_bundle_path.exists():
# Set the environment variable
2024-12-19 12:48:57 +00:00
os.environ["REQUESTS_CA_BUNDLE"] = str(ca_bundle_path)
2024-06-06 07:01:33 +00:00
2024-12-19 12:48:57 +00:00
cert_file_path = lollms_paths.personal_certificates / "cert.pem"
key_file_path = lollms_paths.personal_certificates / "key.pem"
2024-02-14 23:35:04 +00:00
if os.path.exists(cert_file_path) and os.path.exists(key_file_path):
is_https = True
else:
2024-12-19 12:48:57 +00:00
is_https = False
2024-02-14 23:35:04 +00:00
# Create a Socket.IO server
2024-12-19 12:48:57 +00:00
if config["host"] != "localhost":
if config["host"] != "0.0.0.0":
config.allowed_origins.append(
f"https://{config['host']}:{config['port']}"
if is_https
else f"http://{config['host']}:{config['port']}"
)
else:
2024-12-19 12:48:57 +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()
]
allowed_origins = config.allowed_origins + [
(
f"https://localhost:{config['port']}"
if is_https
else f"http://localhost:{config['port']}"
)
]
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-12-19 12:48:57 +00:00
sio = socketio.AsyncServer(
async_mode="asgi",
cors_allowed_origins=allowed_origins,
ping_timeout=1200,
ping_interval=30,
) # Enable CORS for selected origins
# A simple fix for v 11.0 to 12 alpha
2024-12-19 12:48:57 +00:00
if config.rag_vectorizer == "bert":
config.rag_vectorizer = "tfidf"
config.save_config()
2024-12-19 12:48:57 +00:00
LOLLMSWebUI.build_instance(
config=config, lollms_paths=lollms_paths, args=args, sio=sio
)
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
2024-12-19 12:48:57 +00:00
from lollms.server.endpoints.lollms_binding_files_server import \
router as lollms_binding_files_server_router
from lollms.server.endpoints.lollms_binding_infos import \
router as lollms_binding_infos_router
from lollms.server.endpoints.lollms_comfyui import \
router as lollms_comfyui_router
from lollms.server.endpoints.lollms_configuration_infos import \
router as lollms_configuration_infos_router
from lollms.server.endpoints.lollms_diffusers import \
router as lollms_diffusers_router
from lollms.server.endpoints.lollms_discussion import \
router as lollms_discussion_router
from lollms.server.endpoints.lollms_file_system import \
router as lollms_file_system_router
from lollms.server.endpoints.lollms_generator import \
router as lollms_generator_router
from lollms.server.endpoints.lollms_hardware_infos import \
router as lollms_hardware_infos_router
from lollms.server.endpoints.lollms_infos import \
router as lollms_infos_router
from lollms.server.endpoints.lollms_models_infos import \
router as lollms_models_infos_router
from lollms.server.endpoints.lollms_motion_ctrl import \
router as lollms_motion_ctrl_router
from lollms.server.endpoints.lollms_ollama import \
router as lollms_ollama_router
from lollms.server.endpoints.lollms_personalities_infos import \
router as lollms_personalities_infos_router
from lollms.server.endpoints.lollms_petals import \
router as lollms_petals_router
from lollms.server.endpoints.lollms_rag import router as lollms_rag_router
from lollms.server.endpoints.lollms_sd import router as lollms_sd_router
from lollms.server.endpoints.lollms_skills_library import \
router as lollms_skills_library_router
2024-08-25 22:46:26 +00:00
from lollms.server.endpoints.lollms_tti import router as lollms_tti_router
2024-12-19 12:48:57 +00:00
from lollms.server.endpoints.lollms_tts import \
router as lollms_tts_add_router
from lollms.server.endpoints.lollms_user import \
router as lollms_user_router
from lollms.server.endpoints.lollms_vllm import \
router as lollms_vllm_router
2024-09-14 23:37:41 +00:00
from lollms.server.endpoints.lollms_whisper import router as lollms_whisper
2024-12-19 12:48:57 +00:00
from lollms.server.endpoints.lollms_xtts import \
router as lollms_xtts_add_router
from lollms.server.events.lollms_files_events import \
add_events as lollms_files_events_add
from lollms.server.events.lollms_generation_events import \
add_events as lollms_generation_events_add
from lollms.server.events.lollms_model_events import \
add_events as lollms_model_events_add
from lollms.server.events.lollms_personality_events import \
add_events as lollms_personality_events_add
2024-09-14 23:37:41 +00:00
2024-12-19 12:48:57 +00:00
from endpoints.chat_bar import router as chat_bar_router
2024-01-13 09:30:27 +00:00
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-06-25 21:15:43 +00:00
from endpoints.lollms_help import router as help_router
2024-12-19 12:48:57 +00:00
from endpoints.lollms_message import router as lollms_message_router
from endpoints.lollms_playground import router as lollms_playground_router
from endpoints.lollms_webui_infos import \
router as lollms_webui_infos_router
from events.lollms_chatbox_events import \
add_events as lollms_chatbox_events_add
from events.lollms_discussion_events import \
add_events as lollms_webui_discussion_events_add
# from lollms.server.events.lollms_rag_events import add_events as lollms_rag_events_add
from events.lollms_generation_events import \
add_events as lollms_webui_generation_events_add
from events.lollms_interactive_events import \
add_events as lollms_interactive_events_add
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-12-19 12:48:57 +00:00
# Endpoints reserved for local access
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)
2024-12-19 12:48:57 +00:00
app.include_router(lollms_hardware_infos_router)
2024-02-17 23:14:52 +00:00
app.include_router(lollms_binding_infos_router)
2024-12-19 12:48:57 +00:00
app.include_router(lollms_models_infos_router)
app.include_router(lollms_personalities_infos_router)
app.include_router(lollms_skills_library_router)
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-12-19 12:48:57 +00:00
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-12-19 12:48:57 +00:00
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-14 23:35:04 +00:00
2024-12-19 12:48:57 +00:00
app.include_router(lollms_sd_router)
app.include_router(lollms_diffusers_router)
app.include_router(lollms_comfyui_router)
app.include_router(lollms_ollama_router)
app.include_router(lollms_petals_router)
2024-02-14 23:35:04 +00:00
2024-12-19 12:48:57 +00:00
app.include_router(lollms_rag_router)
app.include_router(lollms_vllm_router)
app.include_router(lollms_motion_ctrl_router)
app.include_router(lollms_file_system_router)
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
2024-12-19 12:48:57 +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-03-23 22:50:33 +00:00
lollms_personality_events_add(sio)
lollms_files_events_add(sio)
lollms_model_events_add(sio)
2024-12-19 12:48:57 +00:00
# lollms_rag_events_add(sio)
2024-03-23 22:50:33 +00:00
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
2024-12-19 12:48:57 +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-12-19 12:48:57 +00:00
if path == "":
return FileResponse(
Path(__file__).parent / "web" / "dist" / "index.html",
media_type="text/html",
)
2024-06-06 21:19:03 +00:00
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-12-19 12:48:57 +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
2024-12-19 12:48:57 +00:00
if hasattr(exc, "body"):
2024-12-08 23:29:36 +00:00
return JSONResponse(
status_code=422,
2024-12-19 12:48:57 +00:00
content=jsonable_encoder(
{"detail": exc.errors(), "body": await exc.body}
), # Send the error details and the original request body
2024-12-08 23:29:36 +00:00
)
else:
return JSONResponse(
status_code=422,
2024-12-19 12:48:57 +00:00
content=jsonable_encoder(
{"detail": exc.errors(), "body": ""}
), # Send the error details and the original request body
2024-12-08 23:29:36 +00:00
)
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-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-12-19 12:48:57 +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
2024-12-19 12:48:57 +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-12-19 12:48:57 +00:00
if config["host"] == "0.0.0.0":
webbrowser.open(
f"https://localhost:{config['port']}"
if is_https
else f"http://localhost:{config['port']}"
)
# webbrowser.open(f"http://localhost:{6523}") # needed for debug (to be removed in production)
2024-01-21 04:35:29 +00:00
else:
2024-12-19 12:48:57 +00:00
webbrowser.open(
f"https://{config['host']}:{config['port']}"
if is_https
else f"http://{config['host']}:{config['port']}"
)
# 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:
2024-12-19 12:48:57 +00:00
uvicorn.run(
app,
host=config.host,
port=config.port,
ssl_certfile=cert_file_path,
ssl_keyfile=key_file_path,
)
2024-02-14 23:35:04 +00:00
else:
uvicorn.run(app, host=config.host, port=config.port)
2024-12-19 12:48:57 +00:00
2024-01-13 09:30:27 +00:00
except Exception as ex:
trace_exception(ex)