Fix/cognee UI start backend server (#1380)

<!-- .github/pull_request_template.md -->

## Description
<!-- 
Please provide a clear, human-generated description of the changes in
this PR.
DO NOT use AI-generated descriptions. We want to understand your thought
process and reasoning.
-->

## Type of Change
<!-- Please check the relevant option -->
- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [x] Performance improvement
- [ ] Other (please specify):

## Changes Made
<!-- List the specific changes made in this PR -->
- 
- 
- 

## Testing
<!-- Describe how you tested your changes -->

## Screenshots/Videos (if applicable)
<!-- Add screenshots or videos to help explain your changes -->

## Pre-submission Checklist
<!-- Please check all boxes that apply before submitting your PR -->
- [ ] **I have tested my changes thoroughly before submitting this PR**
- [ ] **This PR contains minimal changes necessary to address the
issue/feature**
- [ ] My code follows the project's coding standards and style
guidelines
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have added necessary documentation (if applicable)
- [ ] All new and existing tests pass
- [ ] I have searched existing PRs to ensure this change hasn't been
submitted already
- [ ] I have linked any relevant issues in the description
- [ ] My commits have clear and descriptive messages

## Related Issues
<!-- Link any related issues using "Fixes #issue_number" or "Relates to
#issue_number" -->

## Additional Notes
<!-- Add any additional notes, concerns, or context for reviewers -->

## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
This commit is contained in:
Vasilije 2025-09-11 11:31:43 -07:00 committed by GitHub
commit 353aa6df7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 30 deletions

View file

@ -7,7 +7,7 @@ import webbrowser
import zipfile
import requests
from pathlib import Path
from typing import Optional, Tuple
from typing import Callable, Optional, Tuple
import tempfile
import shutil
@ -326,6 +326,7 @@ def prompt_user_for_download() -> bool:
def start_ui(
pid_callback: Callable[[int], None],
host: str = "localhost",
port: int = 3000,
open_browser: bool = True,
@ -346,6 +347,7 @@ def start_ui(
6. Optionally open the browser
Args:
pid_callback: Callback to notify with PID of each spawned process
host: Host to bind the frontend server to (default: localhost)
port: Port to run the frontend server on (default: 3000)
open_browser: Whether to open the browser automatically (default: True)
@ -391,20 +393,19 @@ def start_ui(
"--port",
str(backend_port),
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
# Inherit stdout/stderr from parent process to show logs
stdout=None,
stderr=None,
preexec_fn=os.setsid if hasattr(os, "setsid") else None,
)
pid_callback(backend_process.pid)
# Give the backend a moment to start
time.sleep(2)
if backend_process.poll() is not None:
stdout, stderr = backend_process.communicate()
logger.error("Backend server failed to start:")
logger.error(f"stdout: {stdout}")
logger.error(f"stderr: {stderr}")
logger.error("Backend server failed to start - process exited early")
return None
logger.info(f"✓ Backend API started at http://{backend_host}:{backend_port}")
@ -460,7 +461,7 @@ def start_ui(
logger.info("This may take a moment to compile and start...")
try:
# Use process group to ensure all child processes get terminated together
# Create frontend in its own process group for clean termination
process = subprocess.Popen(
["npm", "run", "dev"],
cwd=frontend_path,
@ -468,11 +469,11 @@ def start_ui(
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
preexec_fn=os.setsid
if hasattr(os, "setsid")
else None, # Create new process group on Unix
preexec_fn=os.setsid if hasattr(os, "setsid") else None,
)
pid_callback(process.pid)
# Give it a moment to start up
time.sleep(3)

View file

@ -174,30 +174,23 @@ def main() -> int:
# Handle UI flag
if hasattr(args, "start_ui") and args.start_ui:
server_process = None
spawned_pids = []
def signal_handler(signum, frame):
"""Handle Ctrl+C and other termination signals"""
nonlocal server_process
nonlocal spawned_pids
fmt.echo("\nShutting down UI server...")
if server_process:
for pid in spawned_pids:
try:
# Try graceful termination first
server_process.terminate()
try:
server_process.wait(timeout=5)
fmt.success("UI server stopped gracefully.")
except subprocess.TimeoutExpired:
# If graceful termination fails, force kill
fmt.echo("Force stopping UI server...")
server_process.kill()
server_process.wait()
fmt.success("UI server stopped.")
except Exception as e:
fmt.warning(f"Error stopping server: {e}")
pgid = os.getpgid(pid)
os.killpg(pgid, signal.SIGTERM)
fmt.success(f"✓ Process group {pgid} (PID {pid}) terminated.")
except (OSError, ProcessLookupError) as e:
fmt.warning(f"Could not terminate process {pid}: {e}")
sys.exit(0)
# Set up signal handlers
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
signal.signal(signal.SIGTERM, signal_handler) # Termination request
@ -206,8 +199,18 @@ def main() -> int:
fmt.echo("Starting cognee UI...")
# Callback to capture PIDs of all spawned processes
def pid_callback(pid):
nonlocal spawned_pids
spawned_pids.append(pid)
server_process = start_ui(
host="localhost", port=3000, open_browser=True, start_backend=True
host="localhost",
port=3000,
open_browser=True,
start_backend=True,
auto_download=True,
pid_callback=pid_callback,
)
if server_process:
@ -229,10 +232,12 @@ def main() -> int:
return 0
else:
fmt.error("Failed to start UI server. Check the logs above for details.")
signal_handler(signal.SIGTERM, None)
return 1
except Exception as ex:
fmt.error(f"Error starting UI: {str(ex)}")
signal_handler(signal.SIGTERM, None)
if debug.is_debug_enabled():
raise ex
return 1