AUXteam's picture
Upload folder using huggingface_hub
6e38ce1 verified
Raw
History Blame Contribute Delete
12.8 kB
import os
import warnings
import typer
import uvicorn
from typing_extensions import Annotated
from typing import Optional
from pathlib import Path
import logging
from ..version import VERSION
from .._docker import (
check_docker_running,
check_browser_image,
check_python_image,
pull_browser_image,
pull_python_image,
)
# Configure basic logging to show only errors
logging.basicConfig(level=logging.ERROR)
# Create a Typer application instance with a descriptive help message
# This is the main entry point for CLI commands
app = typer.Typer(help="Magentic-UI: A human-centered interface for web agents.")
# Ignore deprecation warnings from websockets
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated*")
warnings.filterwarnings(
"ignore", message="websockets.server.WebSocketServerProtocol is deprecated*"
)
# Ignore warnings about ffmpeg or avconv not being found
# Audio is not used in the UI, so we can ignore this warning
warnings.filterwarnings("ignore", message="Couldn't find ffmpeg or avconv*")
def get_env_file_path():
"""
Create a temporary environment file path in the user's home directory.
Used to pass environment variables to Uvicorn workers.
Returns:
str: The full path to the temporary environment file
"""
app_dir = os.path.join(os.path.expanduser("~"), ".magentic_ui")
if not os.path.exists(app_dir):
os.makedirs(app_dir, exist_ok=True)
return os.path.join(app_dir, "temp_env_vars.env")
# This decorator makes this function the default action when no subcommand is provided
# invoke_without_command=True means this function runs automatically when only 'magentic-ui' is typed
@app.callback(invoke_without_command=True)
def main(
ctx: typer.Context, # Typer context provides information about the command invocation
host: str = typer.Option("127.0.0.1", help="Host to run the UI on."),
port: int = typer.Option(8081, help="Port to run the UI on."),
workers: int = typer.Option(1, help="Number of workers to run the UI with."),
reload: Annotated[
bool, typer.Option("--reload", help="Reload the UI on code changes.")
] = False,
docs: bool = typer.Option(True, help="Whether to generate API docs."),
appdir: str = typer.Option(
str(Path.home() / ".magentic_ui"),
help="Path to the app directory where files are stored.",
),
database_uri: Optional[str] = typer.Option(
None, "--database-uri", help="Database URI to connect to."
),
upgrade_database: bool = typer.Option(
False, "--upgrade-database", help="Upgrade the database schema on startup."
),
config: Optional[str] = typer.Option(
None, "--config", help="Path to the config file."
),
version: bool = typer.Option(
False, "--version", help="Print the version of Magentic-UI and exit."
),
run_without_docker: Annotated[
bool,
typer.Option(
"--run-without-docker",
help="Run without docker. This will remove coder and filesurfer agents and disable live browser view.",
),
] = False,
fara_agent: Annotated[
bool,
typer.Option(
"--fara",
help="Launch the UI with the FARA-based web surfer agent instead of the default GPT-oriented surfer.",
),
] = False,
):
"""
Magentic-UI: A human-centered interface for web agents.
Run `magentic-ui` to start the application.
"""
# Check if version flag was provided
if version:
typer.echo(f"Magentic-UI version: {VERSION}")
raise typer.Exit()
# This conditional checks if a subcommand was provided
# If no subcommand was specified (e.g., just 'magentic-ui'), run the UI
if ctx.invoked_subcommand is None:
run_ui(
host=host,
port=port,
workers=workers,
reload=reload,
docs=docs,
appdir=appdir,
database_uri=database_uri,
upgrade_database=upgrade_database,
config=config,
run_without_docker=run_without_docker,
fara_agent=fara_agent,
)
def run_ui(
host: str,
port: int,
workers: int,
reload: bool,
docs: bool,
appdir: str,
database_uri: Optional[str],
upgrade_database: bool,
config: Optional[str],
run_without_docker: bool,
fara_agent: bool,
):
"""
Core logic to run the Magentic-UI web application.
This function is used by both the main entry point and the legacy 'ui' command.
Args:
host (str, optional): Host to run the UI on. Defaults to 127.0.0.1 (localhost).
port (int, optional): Port to run the UI on. Defaults to 8081.
workers (int, optional): Number of workers to run the UI with. Defaults to 1.
reload (bool, optional): Whether to reload the UI on code changes. Defaults to False.
docs (bool, optional): Whether to generate API docs. Defaults to True.
appdir (str, optional): Path to the app directory where files are stored. Defaults to ~/.magentic_ui.
database_uri (str, optional): Database URI to connect to. Defaults to None.
upgrade_database (bool, optional): Whether to upgrade the database schema. Defaults to False.
config (str, optional): Path to the LLM config file. Defaults to config.yaml if present.
run_without_docker (bool, optional): Run without docker. This will remove coder and filesurfer agents and disale live browser view. Defaults to False.
fara_agent (bool, optional): Use the FARA-based web surfer agent instead of the default GPT-oriented surfer. Defaults to False.
"""
# Display a green, bold "Starting Magentic-UI" message
typer.echo(typer.style("Starting Magentic-UI", fg=typer.colors.GREEN, bold=True))
# === Docker Setup ===
# Check if Docker is running and prepare required images
if not run_without_docker:
typer.echo("Checking if Docker is running...", nl=False)
if not check_docker_running():
typer.echo(typer.style("Failed\n", fg=typer.colors.RED, bold=True))
typer.echo("Docker is not running. Please start Docker and try again.")
raise typer.Exit(1) # Exit with error code 1
else:
typer.echo(typer.style("OK", fg=typer.colors.GREEN, bold=True))
# Check and pull Docker images if needed
typer.echo("Checking Docker vnc browser image...", nl=False)
if not check_browser_image():
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Pulling Docker vnc image (this WILL take a few minutes)")
pull_browser_image()
typer.echo("\n")
else:
typer.echo(typer.style("OK", fg=typer.colors.GREEN, bold=True))
typer.echo("Checking Docker python image...", nl=False)
if not check_python_image():
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Pulling Docker python image (this WILL take a few minutes)")
pull_python_image()
typer.echo("\n")
else:
typer.echo(typer.style("OK", fg=typer.colors.GREEN, bold=True))
# Verify Docker images exist after attempted pull
if not check_browser_image() or not check_python_image():
typer.echo(typer.style("Failed\n", fg=typer.colors.RED, bold=True))
typer.echo(
"Docker images not found. Please pull or build the images and try again."
)
raise typer.Exit(1)
else:
typer.echo(
typer.style(
"Running without docker... This will remove the live browser view and will disable code and file manipulation.",
fg=typer.colors.YELLOW,
bold=True,
)
)
typer.echo(
typer.style(
"For the full experience of Magentic-UI please use docker.",
fg=typer.colors.YELLOW,
bold=True,
)
)
typer.echo("Launching Web Application...")
# === Environment Setup ===
# Create environment variables to pass to the web application
env_vars = {
"_HOST": host,
"_PORT": port,
"_API_DOCS": str(docs),
}
# Add optional environment variables
if appdir:
env_vars["_APPDIR"] = appdir
if database_uri:
env_vars["DATABASE_URI"] = database_uri
if upgrade_database:
env_vars["_UPGRADE_DATABASE"] = "1"
# Set Docker-related environment variables
env_vars["INSIDE_DOCKER"] = "0"
env_vars["EXTERNAL_WORKSPACE_ROOT"] = appdir
env_vars["INTERNAL_WORKSPACE_ROOT"] = appdir
env_vars["RUN_WITHOUT_DOCKER"] = str(run_without_docker)
env_vars["FARA_AGENT"] = str(fara_agent)
# Handle configuration file path
if not config:
# Look for config.yaml in the current directory if not specified
if os.path.isfile("config.yaml"):
config = "config.yaml"
else:
typer.echo("Config file not provided. Using default settings.")
if config:
env_vars["_CONFIG"] = config
# Create a temporary environment file to share with Uvicorn workers
env_file_path = get_env_file_path()
with open(env_file_path, "w") as temp_env:
for key, value in env_vars.items():
temp_env.write(f"{key}={value}\n")
# Start the Uvicorn server with the configured settings
uvicorn.run(
"magentic_ui.backend.web.app:app", # Path to the ASGI application
host=host,
port=port,
workers=workers,
reload=reload,
reload_excludes=["**/alembic/*", "**/alembic.ini", "**/versions/*"]
if reload
else None,
env_file=env_file_path, # Pass environment variables via file
)
# This command is hidden from help to encourage using the new syntax
# but kept for backward compatibility with existing scripts and documentation
@app.command(hidden=True)
def ui(
host: str = "127.0.0.1",
port: int = 8081,
workers: int = 1,
reload: Annotated[bool, typer.Option("--reload")] = False,
docs: bool = True,
appdir: str = str(Path.home() / ".magentic_ui"),
database_uri: Optional[str] = None,
upgrade_database: bool = False,
config: Optional[str] = None,
run_without_docker: Annotated[
bool,
typer.Option(
"--run-without-docker",
help="Run without docker. This will remove coder and filesurfer agents and disale live browser view.",
),
] = False,
fara_agent: Annotated[
bool,
typer.Option(
"--fara",
help="Launch the UI with the FARA-based web surfer agent instead of the default GPT-oriented surfer.",
),
] = False,
):
"""
[Deprecated] Run Magentic-UI.
This command is kept for backward compatibility.
"""
# Simply delegate to the main run_ui function with the same parameters
run_ui(
host=host,
port=port,
workers=workers,
reload=reload,
docs=docs,
appdir=appdir,
database_uri=database_uri,
upgrade_database=upgrade_database,
config=config,
run_without_docker=run_without_docker,
fara_agent=fara_agent,
)
# Keep the version command for backward compatibility but hide it from help
@app.command(hidden=True)
def version():
"""
Print the version of the Magentic-UI backend CLI.
"""
typer.echo(f"Magentic-UI version: {VERSION}")
@app.command(hidden=True)
def help():
"""
Show help information about available commands and options.
"""
# Use a system call to run the command with --help
import subprocess
import sys
import os
# Get the command that was used to run this script
command = os.path.basename(sys.argv[0])
# If running directly as a module, use the appropriate command name
if command == "python" or command == "python3":
command = "magentic-ui"
# Run the command with --help
try:
subprocess.run([command, "--help"])
except FileNotFoundError:
# Fallback if the command isn't found in PATH
typer.echo(f"Error: Command '{command}' not found in PATH.")
typer.echo(f"For more information, run `{command} --help`")
def run():
"""
Main entry point called by the 'magentic' and 'magentic-ui' commands.
This function is referenced in pyproject.toml's [project.scripts] section.
"""
app() # Hand control to the Typer application
if __name__ == "__main__":
app() # Allow running this file directly with 'python -m magentic_ui.backend.cli'