lollms-webui/endpoints/lollms_apps.py

314 lines
12 KiB
Python
Raw Normal View History

2024-07-30 23:08:35 +00:00
from fastapi import APIRouter, HTTPException, Query
2024-07-29 22:02:24 +00:00
from fastapi.responses import PlainTextResponse
2024-07-28 23:39:19 +00:00
from pydantic import BaseModel, Field
2024-07-27 23:19:18 +00:00
from fastapi.responses import FileResponse
from lollms_webui import LOLLMSWebUI
from pydantic import BaseModel
from pathlib import Path
import shutil
import uuid
import os
import requests
import yaml
2024-07-28 23:39:19 +00:00
from lollms.security import check_access, sanitize_path
2024-07-28 00:49:10 +00:00
import os
import subprocess
import yaml
import uuid
2024-07-29 22:49:52 +00:00
import platform
2024-08-02 07:46:55 +00:00
from ascii_colors import ASCIIColors, trace_exception
2024-08-05 10:10:12 +00:00
import pipmaster as pm
if not pm.is_installed("httpx"):
pm.install("httpx")
import httpx
2024-07-27 23:19:18 +00:00
router = APIRouter()
lollmsElfServer: LOLLMSWebUI = LOLLMSWebUI.get_instance()
class AuthRequest(BaseModel):
client_id: str
class AppInfo:
2024-07-29 23:12:54 +00:00
def __init__(self, uid: str, name: str, folder_name: str, icon: str, category:str, description: str, author:str, version:str, model_name:str, disclaimer:str, installed: bool):
2024-07-27 23:19:18 +00:00
self.uid = uid
self.name = name
2024-07-29 23:12:54 +00:00
self.folder_name = folder_name
2024-07-27 23:19:18 +00:00
self.icon = icon
2024-07-29 22:02:24 +00:00
self.category = category
2024-07-27 23:19:18 +00:00
self.description = description
self.author = author
self.version = version
self.model_name = model_name
self.disclaimer = disclaimer
2024-07-29 23:12:54 +00:00
self.installed = installed
2024-07-27 23:19:18 +00:00
@router.get("/apps")
async def list_apps():
apps = []
2024-08-02 07:46:55 +00:00
apps_zoo_path = lollmsElfServer.lollms_paths.apps_zoo_path
2024-07-27 23:19:18 +00:00
2024-08-02 07:46:55 +00:00
for app_name in apps_zoo_path.iterdir():
2024-07-27 23:19:18 +00:00
if app_name.is_dir():
icon_path = app_name / "icon.png"
description_path = app_name / "description.yaml"
description = ""
author = ""
version = ""
model_name = ""
disclaimer = ""
if description_path.exists():
with open(description_path, 'r') as file:
data = yaml.safe_load(file)
2024-07-29 22:02:24 +00:00
application_name = data.get('name', app_name.name)
category = data.get('category', 'generic')
2024-07-27 23:19:18 +00:00
description = data.get('description', '')
author = data.get('author', '')
version = data.get('version', '')
model_name = data.get('model_name', '')
disclaimer = data.get('disclaimer', 'No disclaimer provided.')
2024-07-29 23:12:54 +00:00
installed = True
else:
installed = False
2024-07-27 23:19:18 +00:00
if icon_path.exists():
uid = str(uuid.uuid4())
apps.append(AppInfo(
uid=uid,
2024-07-29 22:02:24 +00:00
name=application_name,
2024-07-29 23:12:54 +00:00
folder_name = app_name.name,
2024-07-30 23:08:35 +00:00
icon=f"/apps/{app_name.name}/icon.png",
2024-07-29 22:02:24 +00:00
category=category,
2024-07-27 23:19:18 +00:00
description=description,
author=author,
version=version,
model_name=model_name,
2024-07-29 23:12:54 +00:00
disclaimer=disclaimer,
installed=installed
2024-07-27 23:19:18 +00:00
))
return apps
2024-07-29 22:49:52 +00:00
class ShowAppsFolderRequest(BaseModel):
client_id: str = Field(...)
@router.post("/show_apps_folder")
async def open_folder_in_vscode(request: ShowAppsFolderRequest):
check_access(lollmsElfServer, request.client_id)
# Get the current operating system
current_os = platform.system()
try:
if current_os == "Windows":
# For Windows
subprocess.run(['explorer', lollmsElfServer.lollms_paths.apps_zoo_path])
elif current_os == "Darwin":
# For macOS
subprocess.run(['open', lollmsElfServer.lollms_paths.apps_zoo_path])
elif current_os == "Linux":
# For Linux
subprocess.run(['xdg-open', lollmsElfServer.lollms_paths.apps_zoo_path])
else:
print("Unsupported operating system.")
except Exception as e:
print(f"An error occurred: {e}")
2024-07-28 23:39:19 +00:00
class OpenFolderRequest(BaseModel):
client_id: str = Field(...)
app_name: str = Field(...)
@router.post("/open_app_in_vscode")
async def open_folder_in_vscode(request: OpenFolderRequest):
check_access(lollmsElfServer, request.client_id)
sanitize_path(request.app_name)
# Construct the folder path
folder_path = lollmsElfServer.lollms_paths.apps_zoo_path/ request.app_name
# Check if the folder exists
if not folder_path.exists():
raise HTTPException(status_code=404, detail="Folder not found")
# Open the folder in VSCode
try:
os.system(f'code -n "{folder_path}"') # This assumes 'code' is in the PATH
return {"message": f"Opened {folder_path} in VSCode."}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to open folder: {str(e)}")
2024-07-27 23:19:18 +00:00
@router.post("/apps/{app_name}/code")
async def get_app_code(app_name: str, auth: AuthRequest):
2024-07-30 23:08:35 +00:00
check_access(lollmsElfServer, auth.client_id)
2024-07-27 23:19:18 +00:00
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name / "index.html"
if not app_path.exists():
raise HTTPException(status_code=404, detail="App not found")
return FileResponse(app_path)
2024-07-30 23:08:35 +00:00
@router.get("/apps/{app_name}/{file}")
async def get_app_file(app_name: str, file: str):
file=sanitize_path(file)
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name / file
if not app_path.exists():
raise HTTPException(status_code=404, detail="App file not found")
return FileResponse(app_path)
2024-07-27 23:19:18 +00:00
@router.post("/install/{app_name}")
async def install_app(app_name: str, auth: AuthRequest):
2024-07-28 00:49:10 +00:00
check_access(lollmsElfServer, auth.client_id)
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
2024-07-27 23:19:18 +00:00
# Create the app directory
2024-07-28 00:49:10 +00:00
app_path = lollmsElfServer.lollms_paths.apps_zoo_path/app_name # Adjust the path as needed
2024-07-27 23:19:18 +00:00
os.makedirs(app_path, exist_ok=True)
2024-07-28 00:49:10 +00:00
# Define the local paths for the files to copy
files_to_copy = {
"icon.png": REPO_DIR/app_name/"icon.png",
"description.yaml": REPO_DIR/app_name/"description.yaml",
"index.html": REPO_DIR/app_name/"index.html"
2024-07-27 23:19:18 +00:00
}
2024-07-28 00:49:10 +00:00
# Copy each file from the local repo
for file_name, local_path in files_to_copy.items():
if local_path.exists():
with open(local_path, 'rb') as src_file:
with open(app_path/file_name, 'wb') as dest_file:
dest_file.write(src_file.read())
2024-07-27 23:19:18 +00:00
else:
2024-07-28 00:49:10 +00:00
raise HTTPException(status_code=404, detail=f"{file_name} not found in the local repository")
2024-07-27 23:19:18 +00:00
return {"message": f"App {app_name} installed successfully."}
@router.post("/uninstall/{app_name}")
async def uninstall_app(app_name: str, auth: AuthRequest):
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name
if app_path.exists():
shutil.rmtree(app_path)
return {"message": f"App {app_name} uninstalled successfully."}
else:
raise HTTPException(status_code=404, detail="App not found")
2024-07-28 00:49:10 +00:00
REPO_URL = "https://github.com/ParisNeo/lollms_apps_zoo.git"
2024-08-05 10:10:12 +00:00
class ProxyRequest(BaseModel):
url: str
2024-07-28 00:49:10 +00:00
2024-08-05 10:10:12 +00:00
@router.post("/api/proxy")
async def proxy(request: ProxyRequest):
try:
async with httpx.AsyncClient() as client:
response = await client.get(request.url)
return {"content": response.text}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
2024-07-28 00:49:10 +00:00
def clone_repo():
REPO_DIR = Path(lollmsElfServer.lollms_paths.personal_path) / "apps_zoo_repo"
# Check if the directory exists and if it is empty
if REPO_DIR.exists():
if any(REPO_DIR.iterdir()): # Check if the directory is not empty
print(f"Directory {REPO_DIR} is not empty. Aborting clone.")
return
else:
REPO_DIR.mkdir(parents=True, exist_ok=True) # Create the directory if it doesn't exist
# Clone the repository
subprocess.run(["git", "clone", REPO_URL, str(REPO_DIR)], check=True)
print(f"Repository cloned into {REPO_DIR}")
def pull_repo():
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
subprocess.run(["git", "-C", str(REPO_DIR), "pull"], check=True)
def load_apps_data():
apps = []
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
for item in os.listdir(REPO_DIR):
item_path = os.path.join(REPO_DIR, item)
if os.path.isdir(item_path):
description_path = os.path.join(item_path, "description.yaml")
icon_url = f"https://github.com/ParisNeo/lollms_apps_zoo/blob/main/{item}/icon.png?raw=true"
if os.path.exists(description_path):
with open(description_path, 'r') as file:
description_data = yaml.safe_load(file)
apps.append(AppInfo(
uid=str(uuid.uuid4()),
2024-07-29 23:12:54 +00:00
name=description_data.get("name",item),
folder_name=item,
2024-07-28 00:49:10 +00:00
icon=icon_url,
2024-07-29 22:02:24 +00:00
category=description_data.get('category', 'generic'),
2024-07-28 00:49:10 +00:00
description=description_data.get('description', ''),
author=description_data.get('author', ''),
version=description_data.get('version', ''),
model_name=description_data.get('model_name', ''),
2024-07-29 23:12:54 +00:00
disclaimer=description_data.get('disclaimer', 'No disclaimer provided.'),
installed=True
2024-07-28 00:49:10 +00:00
))
return apps
2024-07-29 22:02:24 +00:00
@router.get("/lollms_js", response_class=PlainTextResponse)
async def lollms_js():
# Define the path to the JSON file using pathlib
file_path = Path(__file__).parent / "lollms_client_js.js"
# Read the JSON file
with file_path.open('r') as file:
data = file.read()
return data
@router.get("/lollms_markdown_renderer", response_class=PlainTextResponse)
async def lollms_markdown_renderer():
# Define the path to the JSON file using pathlib
file_path = Path(__file__).parent / "lollms_markdown_renderer.js"
# Read the JSON file
with file_path.open('r') as file:
data = file.read()
return data
@router.get("/lollms_markdown_renderer_css")
async def lollms_markdown_renderer_css():
# Define the path to the CSS file using pathlib
file_path = Path(__file__).parent / "lollms_markdown_renderer.css"
# Use FileResponse to serve the CSS file
return FileResponse(file_path, media_type="text/css")
2024-07-29 22:02:24 +00:00
@router.get("/template")
async def lollms_js():
return {
"start_header_id_template": lollmsElfServer.config.start_header_id_template,
"end_header_id_template": lollmsElfServer.config.end_header_id_template,
"separator_template": lollmsElfServer.config.separator_template,
"start_user_header_id_template": lollmsElfServer.config.start_user_header_id_template,
"end_user_header_id_template": lollmsElfServer.config.end_user_header_id_template,
"end_user_message_id_template": lollmsElfServer.config.end_user_message_id_template,
"start_ai_header_id_template": lollmsElfServer.config.start_ai_header_id_template,
"end_ai_header_id_template": lollmsElfServer.config.end_ai_header_id_template,
"end_ai_message_id_template": lollmsElfServer.config.end_ai_message_id_template,
"system_message_template": lollmsElfServer.config.system_message_template
}
2024-07-28 00:49:10 +00:00
2024-07-27 23:19:18 +00:00
@router.get("/github/apps")
async def fetch_github_apps():
2024-07-28 00:49:10 +00:00
try:
clone_repo()
pull_repo()
2024-08-02 07:46:55 +00:00
except:
ASCIIColors.error("Couldn't interact with ")
lollmsElfServer.error("Couldn't interact with github.\nPlease verify your internet connection")
apps = load_apps_data()
return {"apps": apps}
2024-07-27 23:19:18 +00:00
@router.get("/apps/{app_name}/icon")
async def get_app_icon(app_name: str):
icon_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name / "icon.png"
if not icon_path.exists():
raise HTTPException(status_code=404, detail="Icon not found")
return FileResponse(icon_path)