Skip to content
Merged
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,24 @@ Here's how you can get started with Magentic-UI:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install magentic-ui
pip install magentic-ui --upgrade
# export OPENAI_API_KEY=<YOUR API KEY>
magentic ui --port 8081
magentic-ui --port 8081
```
If your port is 8081, you can then access Magentic-UI at <http://localhost:8081>.


If you are not able to setup Docker, you can run a limited version of Magentic-UI which does not have the ability to execute code, navigate files or display the browser in the interface with the command:

```bash
magentic-ui --run-without-docker --port 8081
```

You can also run Magentic-UI in a command-line-interface:
```bash
magentic-cli --work-dir PATH/TO/STORE/DATA
```

To use Azure models or Ollama please install with the optional dependencies:
```bash
# for Azure
Expand Down Expand Up @@ -174,14 +185,14 @@ uv pip install magentic-ui
To run Magentic-UI, make sure that Docker is running, then run the following command:

```bash
magentic ui --port 8081
magentic-ui --port 8081
```

The first time that you run this command, it will take a while to build the Docker images -- go grab a coffee or something. The next time you run it, it will be much faster as it doesn't have to build the Docker again.

If you have trouble building the dockers, please try to rebuild them with the command:
```bash
magentic ui --rebuild-docker --port 8081
magentic-ui --rebuild-docker --port 8081
```
If you face further issues, please refer to the [TROUBLESHOOTING.md](TROUBLESHOOTING.md) document.

Expand All @@ -200,7 +211,7 @@ magentic-cli --work-dir PATH_TO_STORE_LOGS

If you want to use a different OpenAI key, or if you want to configure use with Azure OpenAI or Ollama, you can do so inside the UI by navigating to settings (top right icon) and changing model configuration with the format of the `config.yaml` file below. You can also create a `config.yaml` and import it inside the UI or point Magentic-UI to its path at startup time:
```bash
magentic ui --config path/to/config.yaml
magentic-ui --config path/to/config.yaml
```

An example `config.yaml` for OpenAI is given below:
Expand Down Expand Up @@ -341,7 +352,7 @@ yarn build
#### 5. Run Magentic-UI, as usual.

```bash
magentic ui --port 8081
magentic-ui --port 8081
```

>**Note**: Running this command for the first time will build two docker containers required for the Magentic-UI agents. If you encounter problems, you can build them directly with the following commands from inside the repository:
Expand Down Expand Up @@ -376,7 +387,7 @@ npm run start
4. Then run the UI:

```bash
magentic ui --port 8081
magentic-ui --port 8081
```

The frontend from source will be available at <http://localhost:8000>, and the compiled frontend will be available at <http://localhost:8081>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ const GeneralSettings: React.FC<GeneralSettingsProps> = ({
/>
</Flex>
<Divider style={{ margin: "0px" }} />
<Flex align="center" justify="space-between" wrap gap="large">
<Flex align="center" justify="start" gap="small" wrap>
Browser Headless
<Tooltip title="Only applicable when running without docker. When enabled, the browser will run in headless mode (no UI).">
<InfoCircleOutlined className="text-secondary hover:text-primary cursor-help" />
</Tooltip>
</Flex>
<Switch
checked={config.browser_headless}
checkedChildren="ON"
unCheckedChildren="OFF"
onChange={(checked) =>
handleUpdateConfig({ browser_headless: checked })
}
/>
</Flex>
<Divider style={{ margin: "0px" }} />

<Flex align="center" justify="space-between" wrap gap="small">
<Flex align="center" gap="small">
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface GeneralConfig {
allow_for_replans: boolean;
do_bing_search: boolean;
websurfer_loop: boolean;
run_without_docker: boolean;
browser_headless: boolean;
model_client_configs: {orchestrator: any, web_surfer: any, coder: any, file_surfer: any, action_guard: any};
mcp_agent_configs: any[];
retrieve_relevant_plans: "never" | "hint" | "reuse"; // this is for using task centric memory to retrieve relevant plans
Expand All @@ -37,6 +39,8 @@ const defaultConfig: GeneralConfig = {
retrieve_relevant_plans: "never",
server_url: "localhost",
mcp_agent_configs: [],
run_without_docker: false,
browser_headless: true,
model_client_configs: {
"orchestrator": DEFAULT_OPENAI,
"web_surfer": DEFAULT_OPENAI,
Expand Down
65 changes: 65 additions & 0 deletions src/magentic_ui/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
from .magentic_ui_config import MagenticUIConfig, ModelClientConfigs
from .types import RunPaths
from .utils import LLMCallFilter
from ._docker import (
check_docker_running,
check_browser_image,
check_python_image,
build_browser_image,
build_python_image,
)

BOLD = "\033[1m"
RESET = "\033[0m"
Expand Down Expand Up @@ -119,6 +126,8 @@ async def get_team(
answer: str | None = None,
mcp_agents: List[McpAgentConfig] | None = None,
use_pretty_ui: bool = True,
run_without_docker: bool = False,
browser_headless: bool = False,
) -> None:
log_debug("=== Starting get_team function ===", debug)
log_debug(
Expand Down Expand Up @@ -238,6 +247,8 @@ async def get_team(
answer=answer,
inside_docker=inside_docker,
mcp_agent_configs=mcp_agents,
run_without_docker=run_without_docker,
browser_headless=browser_headless,
)
log_debug(
f"MagenticUIConfig created with planning={cooperative_planning}, execution={autonomous_execution}",
Expand Down Expand Up @@ -367,6 +378,20 @@ def main() -> None:
default=None,
help="Path to the configuration file (default: 'config.yaml')",
)
parser.add_argument(
"--run-without-docker",
dest="run_without_docker",
action="store_true",
default=False,
help="Run without docker. This will remove coder and filesurfer agents and disable live browser view.",
)
parser.add_argument(
"--headless",
dest="browser_headless",
action="store_true",
default=False,
help="Run browser in headless mode (default: False, browser runs with GUI)",
)
parser.add_argument(
"--debug",
dest="debug",
Expand Down Expand Up @@ -529,6 +554,7 @@ def main() -> None:
log_debug(
f"Console mode: {'Pretty' if args.use_pretty_ui else 'Old'}", args.debug
)
log_debug(f"Browser headless: {args.browser_headless}", args.debug)

# Validate user proxy type
log_debug("Validating user proxy type", args.debug)
Expand Down Expand Up @@ -601,6 +627,43 @@ def main() -> None:
f"Task from argument, length: {len(task if task else '')}", args.debug
)

if not args.run_without_docker:
# Check Docker and build images if necessary
log_debug("Checking Docker setup...", args.debug)
logger.info("Checking if Docker is running...")

if not check_docker_running():
logger.error("Docker is not running. Please start Docker and try again.")
sys.exit(1)
else:
logger.success("Docker is running")

# Check and build Docker images if needed
logger.info("Checking Docker vnc browser image...")
if not check_browser_image():
logger.warning("VNC browser image needs to be built")
logger.info("Building Docker vnc image (this WILL take a few minutes)")
build_browser_image()
else:
logger.success("VNC browser image is available")

logger.info("Checking Docker python image...")
if not check_python_image():
logger.warning("Python image needs to be built")
logger.info("Building Docker python image (this WILL take a few minutes)")
build_python_image()
else:
logger.success("Python image is available")

# Verify Docker images exist after attempted build
if not check_browser_image() or not check_python_image():
logger.error(
"Docker images not found. Please build the images and try again."
)
sys.exit(1)

log_debug("Docker setup completed successfully", args.debug)

log_debug("Processing final answer prompt", args.debug)
final_answer_prompt: str | None = None
if args.final_answer_prompt:
Expand Down Expand Up @@ -689,6 +752,8 @@ def signal_handler(sig: int, frame: types.FrameType | None) -> Any:
answer=args.metadata_answer if args.user_proxy_type == "metadata" else None,
use_pretty_ui=args.use_pretty_ui,
mcp_agents=mcp_agents,
run_without_docker=args.run_without_docker,
browser_headless=args.browser_headless,
)
)
log_debug("Asyncio event loop and get_team function completed", args.debug)
Expand Down
99 changes: 68 additions & 31 deletions src/magentic_ui/backend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def main(
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,
):
"""
Magentic-UI: A human-centered interface for web agents.
Expand All @@ -104,6 +111,7 @@ def main(
upgrade_database=upgrade_database,
config=config,
rebuild_docker=rebuild_docker,
run_without_docker=run_without_docker,
)


Expand All @@ -118,6 +126,7 @@ def run_ui(
upgrade_database: bool,
config: Optional[str],
rebuild_docker: Optional[bool],
run_without_docker: bool,
):
"""
Core logic to run the Magentic-UI web application.
Expand All @@ -134,45 +143,64 @@ def run_ui(
upgrade_database (bool, optional): Whether to upgrade the database schema. Defaults to False.
config (str, optional): Path to the config file. Defaults to config.yaml if present.
rebuild_docker (bool, optional): Rebuild the docker images. Defaults to False.
run_without_docker (bool, optional): Run without docker. This will remove coder and filesurfer agents and disale live browser view. 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
typer.echo("Checking if Docker is running...", nl=False)
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 build Docker images if needed
typer.echo("Checking Docker vnc browser image...", nl=False)
if not check_browser_image() or rebuild_docker:
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Building Docker vnc image (this WILL take a few minutes)")
build_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() or rebuild_docker:
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Building Docker python image (this WILL take a few minutes)")
build_python_image()
typer.echo("\n")
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 build Docker images if needed
typer.echo("Checking Docker vnc browser image...", nl=False)
if not check_browser_image() or rebuild_docker:
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Building Docker vnc image (this WILL take a few minutes)")
build_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() or rebuild_docker:
typer.echo(typer.style("Update\n", fg=typer.colors.YELLOW, bold=True))
typer.echo("Building Docker python image (this WILL take a few minutes)")
build_python_image()
typer.echo("\n")
else:
typer.echo(typer.style("OK", fg=typer.colors.GREEN, bold=True))

# Verify Docker images exist after attempted build
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 build the images and try again."
)
raise typer.Exit(1)
else:
typer.echo(typer.style("OK", fg=typer.colors.GREEN, bold=True))

# Verify Docker images exist after attempted build
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 build the images and try again.")
raise typer.Exit(1)
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...")

Expand All @@ -196,6 +224,7 @@ def run_ui(
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)

# Handle configuration file path
if not config:
Expand Down Expand Up @@ -241,6 +270,13 @@ def ui(
upgrade_database: bool = False,
config: Optional[str] = None,
rebuild_docker: Optional[bool] = False,
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,
):
"""
[Deprecated] Run Magentic-UI.
Expand All @@ -258,6 +294,7 @@ def ui(
upgrade_database=upgrade_database,
config=config,
rebuild_docker=rebuild_docker,
run_without_docker=run_without_docker,
)


Expand Down
Loading