Skip to content

Commit b015744

Browse files
committed
Updated LocalLab v0.1.8
1 parent e9f30d4 commit b015744

File tree

4 files changed

+69
-87
lines changed

4 files changed

+69
-87
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes for version updates.
44

5+
## [0.1.8] - 2025-03-01
6+
7+
### Fixed
8+
9+
- Completely removed health checks and validation when setting up ngrok tunnels
10+
- Fixed issue with logs not appearing correctly due to server starting in a separate process
11+
- Simplified ngrok setup process to run without validation, preventing connection errors during startup
12+
- Improved server startup flow to be more direct, without background health checks or API validation
13+
- Reorganized startup sequence to work properly with ngrok, improving Colab compatibility
14+
515
## [0.1.7] - 2025-03-01
616

717
### Changed

locallab/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
LocalLab - A lightweight AI inference server
33
"""
44

5-
__version__ = "0.1.7"
5+
__version__ = "0.1.8"
66

77
from typing import Dict, Any, Optional
88

locallab/main.py

Lines changed: 57 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,6 @@ async def list_available_models() -> Dict[str, Any]:
221221
@app.get("/health")
222222
async def health_check() -> Dict[str, str]:
223223
"""Check the health status of the server"""
224-
# Always return healthy to ensure the server can respond during startup
225-
# Even if model is still loading
226224
global model_loading
227225
status = "initializing" if model_loading else "healthy"
228226
return {"status": status}
@@ -649,52 +647,24 @@ def delayed_exit():
649647

650648
threading.Thread(target=delayed_exit, daemon=True).start()
651649

652-
def setup_ngrok(port: int = 8000, max_retries: int = 3) -> Optional[str]:
653-
"""Setup ngrok tunnel with retry logic and validation"""
650+
def setup_ngrok(port: int = 8000) -> Optional[str]:
651+
"""Simple ngrok tunnel setup without any validation or health checks"""
654652
ngrok_token = os.getenv("NGROK_AUTH_TOKEN")
655653

656654
if not ngrok_token:
657655
logger.error("NGROK_AUTH_TOKEN environment variable not set")
658656
return None
659657

660-
# Validate token format
661-
if not isinstance(ngrok_token, str) or len(ngrok_token) < 30:
662-
logger.error("Invalid NGROK_AUTH_TOKEN format")
658+
try:
659+
# Configure and start tunnel
660+
conf.get_default().auth_token = ngrok_token
661+
tunnel = ngrok.connect(port, "http")
662+
public_url = tunnel.public_url
663+
logger.info(f"Ngrok tunnel established: {public_url}")
664+
return public_url
665+
except Exception as e:
666+
logger.error(f"Failed to establish ngrok tunnel: {str(e)}")
663667
return None
664-
665-
for attempt in range(max_retries):
666-
try:
667-
# Configure ngrok
668-
conf.get_default().auth_token = ngrok_token
669-
conf.get_default().region = "us" # or other region as needed
670-
671-
# Kill any existing ngrok processes
672-
ngrok.kill()
673-
time.sleep(2) # Added delay to allow previous tunnels to close
674-
675-
# Start new tunnel
676-
tunnel = ngrok.connect(port, "http")
677-
678-
# Verify tunnel
679-
public_url = tunnel.public_url
680-
if not public_url.startswith("http"):
681-
raise ValueError("Invalid tunnel URL")
682-
683-
# Test connection
684-
response = requests.get(f"{public_url}/health", timeout=5)
685-
if response.status_code != 200:
686-
raise ConnectionError("Tunnel health check failed")
687-
688-
logger.info(f"Ngrok tunnel established: {public_url}")
689-
return public_url
690-
691-
except Exception as e:
692-
logger.warning(f"Ngrok connection attempt {attempt + 1} failed: {str(e)}")
693-
if attempt < max_retries - 1:
694-
time.sleep(2 ** attempt) # Exponential backoff
695-
continue
696-
logger.error("Failed to establish ngrok tunnel after all retries")
697-
raise
698668

699669
# New utility functions added to fix undefined errors
700670

@@ -716,9 +686,8 @@ async def load_model_in_background(model_id: str):
716686
# Simplified start_server function that runs directly in the main process
717687
def start_server(use_ngrok: bool = False, port=8000):
718688
"""Start the LocalLab server directly in the main process"""
719-
try:
720-
# Display startup banner
721-
startup_banner = f"""
689+
# Display startup banner
690+
startup_banner = f"""
722691
{Fore.CYAN}
723692
╔══════════════════════════════════════════════════════════════════════╗
724693
║ ║
@@ -728,40 +697,41 @@ def start_server(use_ngrok: bool = False, port=8000):
728697
729698
{Fore.YELLOW}⏳ Initializing server...{Style.RESET_ALL}
730699
"""
731-
print(startup_banner, flush=True)
732-
733-
# Check if port is already in use
734-
if is_port_in_use(port):
735-
logger.warning(f"Port {port} is already in use. Trying to find another port...")
736-
for p in range(port+1, port+100):
737-
if not is_port_in_use(p):
738-
port = p
739-
logger.info(f"Using alternative port: {port}")
740-
break
741-
else:
742-
raise RuntimeError(f"Could not find an available port in range {port}-{port+100}")
743-
744-
# If using ngrok, set up the tunnel before starting the server
745-
if use_ngrok:
746-
logger.info(f"{Fore.CYAN}Setting up ngrok tunnel to port {port}...{Style.RESET_ALL}")
747-
public_url = setup_ngrok(port=port)
748-
if public_url:
749-
ngrok_section = f"\n{Fore.CYAN}┌────────────────────────── Ngrok Tunnel Details ─────────────────────────────┐{Style.RESET_ALL}\n\n│ 🚀 Ngrok Public URL: {Fore.GREEN}{public_url}{Style.RESET_ALL}\n\n{Fore.CYAN}└──────────────────────────────────────────────────────────────────────────────┘{Style.RESET_ALL}\n"
750-
logger.info(ngrok_section)
751-
print(ngrok_section)
752-
else:
753-
logger.error(f"{Fore.RED}Failed to create ngrok tunnel. Check your ngrok token.{Style.RESET_ALL}")
754-
logger.info(f"{Fore.YELLOW}Server will run locally on port {port}.{Style.RESET_ALL}")
755-
756-
# Server info section
757-
server_section = f"\n{Fore.CYAN}┌────────────────────────── Server Details ─────────────────────────────┐{Style.RESET_ALL}\n\n│ 🖥️ Local URL: {Fore.GREEN}http://localhost:{port}{Style.RESET_ALL}\n│ ⚙️ Status: {Fore.GREEN}Running{Style.RESET_ALL}\n│ 🔄 Model Loading: {Fore.YELLOW}In Progress{Style.RESET_ALL}\n\n{Fore.CYAN}└──────────────────────────────────────────────────────────────────────────────┘{Style.RESET_ALL}\n"
758-
print(server_section, flush=True)
759-
760-
# Set up signal handlers for graceful shutdown
761-
signal.signal(signal.SIGINT, signal_handler)
762-
signal.signal(signal.SIGTERM, signal_handler)
763-
764-
# Start uvicorn server directly in the main process
700+
print(startup_banner, flush=True)
701+
702+
# Check if port is already in use
703+
if is_port_in_use(port):
704+
logger.warning(f"Port {port} is already in use. Trying to find another port...")
705+
for p in range(port+1, port+100):
706+
if not is_port_in_use(p):
707+
port = p
708+
logger.info(f"Using alternative port: {port}")
709+
break
710+
else:
711+
raise RuntimeError(f"Could not find an available port in range {port}-{port+100}")
712+
713+
# Server info section
714+
server_section = f"\n{Fore.CYAN}┌────────────────────────── Server Details ─────────────────────────────┐{Style.RESET_ALL}\n\n│ 🖥️ Local URL: {Fore.GREEN}http://localhost:{port}{Style.RESET_ALL}\n│ ⚙️ Status: {Fore.GREEN}Running{Style.RESET_ALL}\n│ 🔄 Model Loading: {Fore.YELLOW}In Progress{Style.RESET_ALL}\n\n{Fore.CYAN}└──────────────────────────────────────────────────────────────────────────────┘{Style.RESET_ALL}\n"
715+
print(server_section, flush=True)
716+
717+
# Set up ngrok before starting server if requested
718+
public_url = None
719+
if use_ngrok:
720+
logger.info(f"{Fore.CYAN}Setting up ngrok tunnel to port {port}...{Style.RESET_ALL}")
721+
public_url = setup_ngrok(port=port)
722+
if public_url:
723+
ngrok_section = f"\n{Fore.CYAN}┌────────────────────────── Ngrok Tunnel Details ─────────────────────────────┐{Style.RESET_ALL}\n\n│ 🚀 Ngrok Public URL: {Fore.GREEN}{public_url}{Style.RESET_ALL}\n\n{Fore.CYAN}└──────────────────────────────────────────────────────────────────────────────┘{Style.RESET_ALL}\n"
724+
logger.info(ngrok_section)
725+
print(ngrok_section)
726+
else:
727+
logger.warning(f"{Fore.YELLOW}Failed to set up ngrok tunnel. Server will run locally on port {port}.{Style.RESET_ALL}")
728+
729+
# Set up signal handlers for graceful shutdown
730+
signal.signal(signal.SIGINT, signal_handler)
731+
signal.signal(signal.SIGTERM, signal_handler)
732+
733+
# Start uvicorn server directly in the main process
734+
try:
765735
if use_ngrok:
766736
# Colab environment setup
767737
import nest_asyncio
@@ -774,17 +744,19 @@ def start_server(use_ngrok: bool = False, port=8000):
774744
# Local environment
775745
logger.info(f"Starting server on port {port} (local mode)")
776746
uvicorn.run(app, host="127.0.0.1", port=port, reload=False, workers=1, log_level="info")
777-
778747
except Exception as e:
748+
# Clean up ngrok if server fails to start
749+
if use_ngrok and public_url:
750+
try:
751+
ngrok.disconnect(public_url)
752+
except:
753+
pass
779754
logger.error(f"Server startup failed: {str(e)}")
780755
logger.error(traceback.format_exc())
781756
raise
782757

783758
if __name__ == "__main__":
784-
# Setup signal handlers
785-
signal.signal(signal.SIGINT, signal_handler)
786-
signal.signal(signal.SIGTERM, signal_handler)
787-
788-
# Start the server with ngrok if in Colab
759+
# Start the server with ngrok if requested via environment variable
789760
use_ngrok = os.getenv("ENABLE_NGROK", "false").lower() == "true"
790-
start_server(use_ngrok=use_ngrok)
761+
port = int(os.getenv("SERVER_PORT", "8000"))
762+
start_server(use_ngrok=use_ngrok, port=port)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name="locallab",
8-
version="0.1.7",
8+
version="0.1.8",
99
packages=find_packages(include=["locallab", "locallab.*"]),
1010
install_requires=[
1111
"fastapi>=0.68.0,<1.0.0",

0 commit comments

Comments
 (0)