File size: 7,381 Bytes
6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 6a50e97 4d77f4f 360b135 4d77f4f 360b135 4d77f4f 360b135 4d77f4f 6a50e97 4d77f4f 360b135 6a50e97 4d77f4f 6a50e97 360b135 4d77f4f 6a50e97 4d77f4f 6a50e97 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
"""
Edge LLM API - Main application entry point with integrated frontend
This entry point handles both backend API and frontend serving,
with automatic port detection and process management.
"""
import uvicorn
import socket
import subprocess
import sys
import os
import time
import signal
import webbrowser
from backend.main import app
def find_free_port(start_port=8000, max_attempts=50):
"""Find a free port starting from start_port"""
for port in range(start_port, start_port + max_attempts):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('localhost', port))
return port
except OSError:
continue
raise RuntimeError(f"Could not find a free port in range {start_port}-{start_port + max_attempts}")
def kill_processes_on_port(port):
"""Kill processes using the specified port"""
try:
if os.name == 'nt': # Windows
result = subprocess.run(['netstat', '-ano'], capture_output=True, text=True)
lines = result.stdout.split('\n')
for line in lines:
if f':{port}' in line and 'LISTENING' in line:
parts = line.split()
if len(parts) >= 5:
pid = parts[-1]
try:
subprocess.run(['taskkill', '/pid', pid, '/f'],
capture_output=True, check=True)
print(f"β
Killed process {pid} on port {port}")
except subprocess.CalledProcessError:
pass
else: # Unix/Linux/macOS
try:
result = subprocess.run(['lsof', '-ti', f':{port}'],
capture_output=True, text=True)
pids = result.stdout.strip().split('\n')
for pid in pids:
if pid:
subprocess.run(['kill', '-9', pid], capture_output=True)
print(f"β
Killed process {pid} on port {port}")
except subprocess.CalledProcessError:
pass
except Exception as e:
print(f"β οΈ Warning: Could not kill processes on port {port}: {e}")
def update_frontend_config(port):
"""Update frontend configuration to use the correct backend port"""
frontend_files = [
'frontend/src/pages/Models.tsx',
'frontend/src/pages/Playground.tsx'
]
for file_path in frontend_files:
if os.path.exists(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Update the baseUrl to use the current port (no longer needed with dynamic ports)
old_pattern = "window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''"
new_pattern = old_pattern # No change needed since it's already dynamic
# No need to update frontend files since they use dynamic origins now
print(f"β
Frontend uses dynamic origins - no port updates needed")
except Exception as e:
print(f"β οΈ Warning: Could not update {file_path}: {e}")
def build_frontend():
"""Build the frontend if needed"""
if not os.path.exists('frontend/dist') or not os.listdir('frontend/dist'):
print("π¨ Building frontend...")
try:
os.chdir('frontend')
subprocess.run(['npm', 'install'], check=True, capture_output=True)
subprocess.run(['npm', 'run', 'build'], check=True, capture_output=True)
os.chdir('..')
print("β
Frontend built successfully")
except subprocess.CalledProcessError as e:
print(f"β Frontend build failed: {e}")
os.chdir('..')
return False
except FileNotFoundError:
print("β npm not found. Please install Node.js")
return False
return True
def should_rebuild_frontend():
"""Check if frontend needs to be rebuilt"""
# Check if build exists
if not (os.path.exists('frontend/dist/index.html') and os.path.exists('frontend/dist/assets')):
print("β οΈ Frontend build not found - will build it")
return True
# Check if source is newer than build
try:
dist_time = os.path.getmtime('frontend/dist/index.html')
# Check key source files
source_files = [
'frontend/src',
'frontend/package.json',
'frontend/vite.config.ts',
'frontend/tsconfig.json'
]
for src_path in source_files:
if os.path.exists(src_path):
if os.path.isdir(src_path):
# Check all files in directory
for root, dirs, files in os.walk(src_path):
for file in files:
file_path = os.path.join(root, file)
if os.path.getmtime(file_path) > dist_time:
print(f"π Source files changed - will rebuild frontend")
return True
else:
if os.path.getmtime(src_path) > dist_time:
print(f"π {src_path} changed - will rebuild frontend")
return True
print("β
Frontend build is up to date")
return False
except Exception as e:
print(f"β οΈ Error checking build status: {e} - will rebuild")
return True
def cleanup_handler(signum, frame):
"""Handle cleanup on exit"""
print("\nπ Shutting down Edge LLM...")
sys.exit(0)
if __name__ == "__main__":
# Set up signal handlers
signal.signal(signal.SIGINT, cleanup_handler)
signal.signal(signal.SIGTERM, cleanup_handler)
print("π Starting Edge LLM...")
# Use HF Spaces default port (7860) or environment variable
port = int(os.getenv("PORT", "7860"))
print(f"π‘ Using port: {port}")
# For Hugging Face Spaces, skip complex port logic and auto-build
is_hf_space = os.getenv("SPACE_ID") is not None
if not is_hf_space:
# Local development: kill existing processes and auto-build
kill_processes_on_port(port)
# Auto-build frontend if needed
if should_rebuild_frontend():
print("π¨ Building frontend...")
build_frontend()
# Auto-open browser for local development
def open_browser():
time.sleep(2)
webbrowser.open(f'http://localhost:{port}')
import threading
browser_thread = threading.Thread(target=open_browser)
browser_thread.daemon = True
browser_thread.start()
try:
# Start the backend server
print(f"π Starting server on http://{'0.0.0.0' if is_hf_space else 'localhost'}:{port}")
print("π― Frontend and Backend integrated - ready to use!")
# Start the server
uvicorn.run(app, host="0.0.0.0", port=port)
except Exception as e:
print(f"β Error starting server: {e}")
sys.exit(1)
|