upgraded code

This commit is contained in:
Saifeddine ALOUI 2024-08-11 21:55:00 +02:00
parent a2582a650b
commit ef7f0f30ad
7 changed files with 215 additions and 111 deletions

View File

@ -1,13 +1,9 @@
from fastapi import APIRouter, HTTPException
from fastapi.responses import PlainTextResponse
from pathlib import Path
from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import PlainTextResponse
from fastapi import FastAPI, File, UploadFile, HTTPException, APIRouter, BackgroundTasks
from fastapi.responses import JSONResponse, StreamingResponse, PlainTextResponse
from pydantic import BaseModel, Field
from fastapi.responses import FileResponse
from lollms_webui import LOLLMSWebUI
from pydantic import BaseModel
from packaging import version
from pathlib import Path
import shutil
import uuid
@ -17,7 +13,6 @@ import yaml
from lollms.security import check_access, sanitize_path
import os
import subprocess
import yaml
import uuid
import platform
from ascii_colors import ASCIIColors, trace_exception
@ -34,13 +29,15 @@ def check_lollms_models_zoo():
ASCIIColors.execute_with_animation("Checking zip library.", check_lollms_models_zoo)
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from pathlib import Path
import zipfile
import io
import shutil
router = APIRouter()
lollmsElfServer: LOLLMSWebUI = LOLLMSWebUI.get_instance()
@ -48,7 +45,23 @@ class AuthRequest(BaseModel):
client_id: str
class AppInfo:
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):
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,
has_server:bool,
is_public:bool,
has_update:bool,
installed: bool
):
self.uid = uid
self.name = name
self.folder_name = folder_name
@ -59,22 +72,31 @@ class AppInfo:
self.version = version
self.model_name = model_name
self.disclaimer = disclaimer
self.has_server = has_server
self.has_update = has_update
self.is_public = is_public
self.installed = installed
@router.get("/apps")
async def list_apps():
apps = []
apps_zoo_path = lollmsElfServer.lollms_paths.apps_zoo_path
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
if REPO_DIR.exists():
remote_apps = [a.stem for a in REPO_DIR.iterdir()]
else:
remote_apps = []
for app_name in apps_zoo_path.iterdir():
if app_name.is_dir():
icon_path = app_name / "icon.png"
description_path = app_name / "description.yaml"
description = ""
author = ""
version = ""
current_version = ""
model_name = ""
disclaimer = ""
has_server = False
is_public = app_name.stem in remote_apps
if description_path.exists():
with open(description_path, 'r') as file:
@ -83,13 +105,29 @@ async def list_apps():
category = data.get('category', 'generic')
description = data.get('description', '')
author = data.get('author', '')
version = data.get('version', '')
current_version = data.get('version', '')
model_name = data.get('model_name', '')
disclaimer = data.get('disclaimer', 'No disclaimer provided.')
has_server = data.get('has_server', False)
installed = True
else:
installed = False
if is_public:
try:
with (REPO_DIR / app_name / "description.yaml").open("r") as file:
# Parse the YAML content
yaml_content = yaml.safe_load(file)
repo_version = yaml_content.get("version", "0")
# Compare versions using packaging.version
has_update = version.parse(str(repo_version)) > version.parse(str(current_version))
except (yaml.YAMLError, FileNotFoundError) as e:
print(f"Error reading or parsing YAML file: {e}")
has_update = False
else:
has_update = False
if icon_path.exists():
uid = str(uuid.uuid4())
apps.append(AppInfo(
@ -100,9 +138,12 @@ async def list_apps():
category=category,
description=description,
author=author,
version=version,
version=current_version,
model_name=model_name,
disclaimer=disclaimer,
has_server=has_server,
is_public=is_public,
has_update=has_update,
installed=installed
))
@ -197,13 +238,6 @@ async def download_app(input_data: AppNameInput):
headers={"Content-Disposition": f"attachment; filename={app_name}.zip"}
)
import os
import yaml
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
import zipfile
import shutil
@router.post("/upload_app")
async def upload_app(client_id: str, file: UploadFile = File(...)):
check_access(lollmsElfServer, client_id)
@ -352,6 +386,9 @@ def load_apps_data():
version=description_data.get('version', ''),
model_name=description_data.get('model_name', ''),
disclaimer=description_data.get('disclaimer', 'No disclaimer provided.'),
has_server=description_data.get('has_server', False),
is_public=True,
has_update=False,
installed=True
))
return apps
@ -416,9 +453,26 @@ async def fetch_github_apps():
apps = load_apps_data()
return {"apps": apps}
@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)
def run_server(app_path: Path):
server_script = app_path / "server.py"
if server_script.exists():
subprocess.Popen(["python", str(server_script)], cwd=str(app_path))
else:
print(f"Server script not found for app: {app_path.name}")
@router.post("/apps/start_server")
async def start_app_server(request: OpenFolderRequest):
check_access(lollmsElfServer, request.client_id)
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / request.app_name
if not app_path.exists():
raise HTTPException(status_code=404, detail="App not found")
server_script = app_path / "server.py"
if not server_script.exists():
raise HTTPException(status_code=404, detail="Server script not found for this app")
# Start the server in the background
background_tasks.add_task(run_server, app_path)
return {"status": "success", "message": f"Server for {app_name} is starting"}

View File

@ -35,4 +35,4 @@ scrapemaster
aiofiles
python-multipart
zipfile36
zipfile36

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
web/dist/assets/index-bfc4ff0e.css vendored Normal file

File diff suppressed because one or more lines are too long

4
web/dist/index.html vendored
View File

@ -6,8 +6,8 @@
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMS WebUI</title>
<script type="module" crossorigin src="/assets/index-c846d59c.js"></script>
<link rel="stylesheet" href="/assets/index-22501ef4.css">
<script type="module" crossorigin src="/assets/index-7542a266.js"></script>
<link rel="stylesheet" href="/assets/index-bfc4ff0e.css">
</head>
<body>
<div id="app"></div>

View File

@ -50,79 +50,101 @@
<span class="text-xl text-gray-700 font-semibold">Loading...</span>
</div>
<div v-for="category in categories" :key="category" class="mb-12">
<h2 class="text-3xl font-bold mb-6 text-gray-800">{{ category }}</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div
v-for="app in filteredApps.filter(a => a.category === category)"
:key="app.uid"
class="app-card bg-white border rounded-xl shadow-lg p-6 hover:shadow-xl transition duration-300 ease-in-out flex flex-col h-full"
>
<div class="flex-grow">
<div class="flex items-center mb-4">
<img :src="app.icon" alt="App Icon" class="w-16 h-16 rounded-full border border-gray-300 mr-4" />
<div>
<h3 class="font-bold text-xl text-gray-800">{{ app.name }}</h3>
<p class="text-sm text-gray-600">Author: {{ app.author }}</p>
<p class="text-sm text-gray-600">Version: {{ app.version }}</p>
</div>
</div>
<div class="mb-4">
<h4 class="font-semibold mb-1 text-gray-700">Description:</h4>
<p class="text-sm text-gray-600 h-20 overflow-y-auto">{{ app.description }}</p>
</div>
<p class="text-sm text-gray-600 mb-2">AI Model: {{ app.model_name }}</p>
<div v-if="app.disclaimer && app.disclaimer.trim() !== ''" class="mb-4">
<h4 class="font-semibold mb-1 text-gray-700">Disclaimer:</h4>
<p class="text-xs text-gray-500 italic h-16 overflow-y-auto">{{ app.disclaimer }}</p>
</div>
</div>
<div class="mt-auto pt-4 border-t">
<div class="flex justify-between">
<button v-if="app.installed" @click="uninstallApp(app.folder_name)" class="text-red-500 hover:text-red-600 transition duration-300 ease-in-out" title="Uninstall">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<button v-else-if="app.existsInFolder" @click="deleteApp(app.name)" class="text-yellow-500 hover:text-yellow-600 transition duration-300 ease-in-out" title="Delete">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<button v-else @click="installApp(app.folder_name)" class="text-blue-500 hover:text-blue-600 transition duration-300 ease-in-out" title="Install">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
<button v-if="app.installed" @click="editApp(app)" class="text-purple-500 hover:text-purple-600 transition duration-300 ease-in-out" title="Edit">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button @click="downloadApp(app.folder_name)" class="text-green-500 hover:text-green-600 transition duration-300 ease-in-out" title="Download">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
<button @click="handleAppClick(app)" class="text-gray-500 hover:text-gray-600 transition duration-300 ease-in-out" title="View">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</button>
<button @click="openApp(app)" class="text-indigo-500 hover:text-indigo-600 transition duration-300 ease-in-out" title="Open">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</button>
</div>
<h2 class="text-3xl font-bold mb-6 text-gray-800">{{ category }}</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div
v-for="app in filteredApps.filter(a => a.category === category)"
:key="app.uid"
class="app-card bg-white border rounded-xl shadow-lg p-6 hover:shadow-xl transition duration-300 ease-in-out flex flex-col h-full"
>
<div class="flex-grow">
<div class="flex items-center mb-4">
<img :src="app.icon" alt="App Icon" class="w-16 h-16 rounded-full border border-gray-300 mr-4" />
<div>
<h3 class="font-bold text-xl text-gray-800">{{ app.name }}</h3>
<p class="text-sm text-gray-600">Author: {{ app.author }}</p>
<p class="text-sm text-gray-600">Version: {{ app.version }}</p>
<!-- New section for app visibility -->
<p class="text-sm" :class="app.is_public ? 'text-green-600' : 'text-orange-600'">
{{ app.is_public ? 'Public App' : 'Local App' }}
</p>
</div>
</div>
<div class="mb-4">
<h4 class="font-semibold mb-1 text-gray-700">Description:</h4>
<p class="text-sm text-gray-600 h-20 overflow-y-auto">{{ app.description }}</p>
</div>
<p class="text-sm text-gray-600 mb-2">AI Model: {{ app.model_name }}</p>
<div v-if="app.disclaimer && app.disclaimer.trim() !== ''" class="mb-4">
<h4 class="font-semibold mb-1 text-gray-700">Disclaimer:</h4>
<p class="text-xs text-gray-500 italic h-16 overflow-y-auto">{{ app.disclaimer }}</p>
</div>
</div>
<div class="mt-auto pt-4 border-t">
<div class="flex justify-between items-center flex-wrap">
<button v-if="app.installed" @click="uninstallApp(app.folder_name)" class="text-red-500 hover:text-red-600 transition duration-300 ease-in-out" title="Uninstall">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<button v-else-if="app.existsInFolder" @click="deleteApp(app.name)" class="text-yellow-500 hover:text-yellow-600 transition duration-300 ease-in-out" title="Delete">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<button v-else @click="installApp(app.folder_name)" class="text-blue-500 hover:text-blue-600 transition duration-300 ease-in-out" title="Install">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
<button v-if="app.installed" @click="editApp(app)" class="text-purple-500 hover:text-purple-600 transition duration-300 ease-in-out" title="Edit">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button @click="downloadApp(app.folder_name)" class="text-green-500 hover:text-green-600 transition duration-300 ease-in-out" title="Download">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
<button @click="handleAppClick(app)" class="text-gray-500 hover:text-gray-600 transition duration-300 ease-in-out" title="View">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</button>
<button @click="openApp(app)" class="text-indigo-500 hover:text-indigo-600 transition duration-300 ease-in-out" title="Open">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</button>
<!-- New button for starting the server -->
<button v-if="app.has_server && app.installed" @click="startServer(app.folder_name)" class="text-teal-500 hover:text-teal-600 transition duration-300 ease-in-out" title="Start Server">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M12 5l7 7-7 7" />
</svg>
</button>
<!-- New button for updating the app -->
<button v-if="app.has_update" @click="updateApp(app.folder_name)" class="relative text-yellow-500 hover:text-yellow-600 transition duration-300 ease-in-out animate-pulse" title="Update Available">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
<span class="absolute top-0 right-0 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-red-100 transform translate-x-1/2 -translate-y-1/2 bg-red-600 rounded-full">!</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div v-if="selectedApp" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 w-11/12 h-5/6 flex flex-col">
@ -185,7 +207,35 @@ export default {
}
},
methods: {
triggerFileInput() {
startServer(appName) {
const payload = {
client_id: this.$store.state.client_id, // Assuming you have a clientId property in your component
app_name: appName
};
axios.post(`/apps/start_server`, payload)
.then(response => {
// Handle successful server start
console.log('Server start initiated:', response.data.message);
// You might want to show a notification to the user here
this.$notify({
type: 'success',
title: 'Server Starting',
text: response.data.message
});
})
.catch(error => {
// Handle error
console.error('Error starting server:', error);
// Show an error notification
this.$notify({
type: 'error',
title: 'Server Start Failed',
text: error.response?.data?.detail || 'An error occurred while starting the server'
});
});
},
triggerFileInput() {
this.$refs.fileInput.click();
},
onFileSelected(event) {