From d4f74b05b07126f272d6a8562c0996146476167f Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 3 Mar 2026 23:47:27 -0500 Subject: [PATCH] feat: enhance LocalAGI installation and update scripts with source build and systemd service integration --- ct/localagi.sh | 100 ++++++++++++++------------ frontend/public/json/localagi.json | 13 ++-- install/localagi-install.sh | 111 ++++++++++++++++++----------- 3 files changed, 132 insertions(+), 92 deletions(-) diff --git a/ct/localagi.sh b/ct/localagi.sh index d7762184f..c84ed92f3 100644 --- a/ct/localagi.sh +++ b/ct/localagi.sh @@ -44,29 +44,25 @@ resolve_backend() { echo "$backend" } -compose_file_for_backend() { - case "$1" in - cu128) - echo "docker-compose.nvidia.yaml" - ;; - rocm7.2) - echo "docker-compose.amd.yaml" - ;; - *) - echo "docker-compose.yaml" - ;; - esac +set_env_var() { + local env_file="$1" + local key="$2" + local value="$3" + if grep -q "^${key}=" "$env_file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$env_file" + else + echo "${key}=${value}" >>"$env_file" + fi } -run_compose() { - if docker compose version >/dev/null 2>&1; then - docker compose "$@" - elif command -v docker-compose >/dev/null 2>&1; then - docker-compose "$@" - else - msg_error "Docker Compose is not available" - return 1 - fi +build_localagi_source() { + msg_info "Building LocalAGI from source" + cd /opt/localagi/webui/react-ui || return 1 + $STD bun install || return 1 + $STD bun run build || return 1 + cd /opt/localagi || return 1 + $STD go build -o /usr/local/bin/localagi || return 1 + msg_ok "Built LocalAGI from source" } function update_script() { @@ -74,7 +70,7 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /opt/localagi/docker-compose.yaml ]]; then + if [[ ! -d /opt/localagi || ! -f /etc/systemd/system/localagi.service ]]; then msg_error "No ${APP} Installation Found!" exit fi @@ -83,45 +79,57 @@ function update_script() { if check_for_gh_release "localagi" "mudler/LocalAGI"; then update_performed="yes" - msg_info "Stopping LocalAGI Stack" - cd /opt/localagi || exit - CURRENT_COMPOSE_FILE="$(cat /opt/localagi/.compose_file 2>/dev/null || echo docker-compose.yaml)" - run_compose -f "$CURRENT_COMPOSE_FILE" down || true - msg_ok "Stopped LocalAGI Stack" + msg_info "Stopping LocalAGI Service" + systemctl stop localagi + msg_ok "Stopped LocalAGI Service" + + msg_info "Backing up Environment" + cp /opt/localagi/.env /tmp/localagi.env.backup 2>/dev/null || true + msg_ok "Backed up Environment" msg_info "Updating LocalAGI" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "localagi" "mudler/LocalAGI" "tarball" "latest" "/opt/localagi" msg_ok "Updated LocalAGI" + + if [[ -f /tmp/localagi.env.backup ]]; then + msg_info "Restoring Environment" + cp /tmp/localagi.env.backup /opt/localagi/.env + rm -f /tmp/localagi.env.backup + msg_ok "Restored Environment" + fi fi BACKEND="$(resolve_backend)" - COMPOSE_FILE="$(compose_file_for_backend "$BACKEND")" - - if [[ ! -f "/opt/localagi/${COMPOSE_FILE}" ]]; then - msg_warn "Compose profile ${COMPOSE_FILE} not found, falling back to CPU profile" - BACKEND="cpu" - COMPOSE_FILE="docker-compose.yaml" - fi - - echo "$BACKEND" >/opt/localagi/.backend - echo "$COMPOSE_FILE" >/opt/localagi/.compose_file - - msg_info "Deploying LocalAGI (${BACKEND})" - cd /opt/localagi || exit - if ! run_compose -f "$COMPOSE_FILE" pull; then - msg_error "Failed to pull LocalAGI images" + if [[ ! -f /opt/localagi/.env ]]; then + msg_warn "Missing /opt/localagi/.env. Recreate by running install script again." exit fi - if ! run_compose -f "$COMPOSE_FILE" up -d; then - msg_error "Failed to start LocalAGI stack" + + NODE_VERSION="24" setup_nodejs + GO_VERSION="latest" setup_go + if ! command -v bun >/dev/null 2>&1; then + msg_info "Installing Bun" + $STD npm install -g bun + msg_ok "Installed Bun" + fi + + set_env_var /opt/localagi/.env "LOCALAGI_GPU_BACKEND" "$BACKEND" + if ! build_localagi_source; then + msg_error "Failed to build LocalAGI from source" exit fi - msg_ok "Deployed LocalAGI (${BACKEND})" + + msg_info "Starting LocalAGI Service" + if ! systemctl restart localagi; then + msg_error "Failed to start LocalAGI service" + exit + fi + msg_ok "Started LocalAGI (${BACKEND})" if [[ "$update_performed" == "yes" ]]; then msg_ok "Updated successfully!" else - msg_ok "No update required. Reapplied compose profile successfully." + msg_ok "No update required. Rebuilt source and restarted service." fi exit } diff --git a/frontend/public/json/localagi.json b/frontend/public/json/localagi.json index b9e4d3695..b1f7ff164 100644 --- a/frontend/public/json/localagi.json +++ b/frontend/public/json/localagi.json @@ -2,8 +2,7 @@ "name": "LocalAGI", "slug": "localagi", "categories": [ - 20, - 3 + 20 ], "date_created": "2026-03-03", "type": "ct", @@ -13,7 +12,7 @@ "documentation": "https://github.com/mudler/LocalAGI#installation-options", "website": "https://github.com/mudler/LocalAGI", "logo": "https://github.com/mudler/LocalAGI/raw/main/webui/react-ui/public/logo_1.png", - "config_path": "/opt/localagi/docker-compose.yaml", + "config_path": "/opt/localagi/.env", "description": "LocalAGI is a self-hostable AI agent platform with a web UI, OpenAI-compatible APIs, and local-first model orchestration.", "install_methods": [ { @@ -34,11 +33,15 @@ }, "notes": [ { - "text": "GPU passthrough can be enabled during CT creation. Backend auto-selects (`cu128` for NVIDIA, `rocm7.2` for AMD) and falls back to CPU.", + "text": "This script builds LocalAGI from source (Go + Bun) and runs it as a systemd service.", "type": "info" }, { - "text": "Set `var_localagi_backend=cpu|cu128|rocm7.2` (or `var_torch_backend`) to force a specific backend profile.", + "text": "GPU passthrough can be enabled during CT creation. `var_localagi_backend=cpu|cu128|rocm7.2` is recorded as `LOCALAGI_GPU_BACKEND` for your runtime setup.", + "type": "info" + }, + { + "text": "By default, LocalAGI is configured to call an external OpenAI-compatible backend at `http://127.0.0.1:8081` via `LOCALAGI_LLM_API_URL`.", "type": "info" } ] diff --git a/install/localagi-install.sh b/install/localagi-install.sh index 2b3a08332..664ff4982 100644 --- a/install/localagi-install.sh +++ b/install/localagi-install.sh @@ -6,12 +6,14 @@ # Source: https://github.com/mudler/LocalAGI source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +APP="LocalAGI" color verb_ip6 catch_errors setting_up_container network_check update_os +header_info "$APP" resolve_backend() { local requested="${var_localagi_backend:-${var_torch_backend:-auto}}" @@ -35,29 +37,25 @@ resolve_backend() { echo "$backend" } -compose_file_for_backend() { - case "$1" in - cu128) - echo "docker-compose.nvidia.yaml" - ;; - rocm7.2) - echo "docker-compose.amd.yaml" - ;; - *) - echo "docker-compose.yaml" - ;; - esac +set_env_var() { + local env_file="$1" + local key="$2" + local value="$3" + if grep -q "^${key}=" "$env_file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$env_file" + else + echo "${key}=${value}" >>"$env_file" + fi } -run_compose() { - if docker compose version >/dev/null 2>&1; then - docker compose "$@" - elif command -v docker-compose >/dev/null 2>&1; then - docker-compose "$@" - else - msg_error "Docker Compose is not available" - return 1 - fi +build_localagi_source() { + msg_info "Building LocalAGI from source" + cd /opt/localagi/webui/react-ui || return 1 + $STD bun install || return 1 + $STD bun run build || return 1 + cd /opt/localagi || return 1 + $STD go build -o /usr/local/bin/localagi || return 1 + msg_ok "Built LocalAGI from source" } msg_info "Installing Dependencies" @@ -65,36 +63,67 @@ $STD apt install -y \ curl \ ca-certificates \ git \ - jq + jq \ + build-essential msg_ok "Installed Dependencies" -msg_info "Installing Docker" -setup_docker -msg_ok "Installed Docker" +NODE_VERSION="24" setup_nodejs +GO_VERSION="latest" setup_go -msg_info "Installing LocalAGI" +msg_info "Installing Bun" +if ! command -v bun >/dev/null 2>&1; then + $STD npm install -g bun +fi +msg_ok "Installed Bun" + +msg_info "Fetching LocalAGI Source" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "localagi" "mudler/LocalAGI" "tarball" "latest" "/opt/localagi" -msg_ok "Installed LocalAGI" +msg_ok "Fetched LocalAGI Source" BACKEND="$(resolve_backend)" -COMPOSE_FILE="$(compose_file_for_backend "$BACKEND")" -if [[ ! -f "/opt/localagi/${COMPOSE_FILE}" ]]; then - msg_warn "Compose profile ${COMPOSE_FILE} not found, falling back to CPU profile" - BACKEND="cpu" - COMPOSE_FILE="docker-compose.yaml" -fi +mkdir -p /opt/localagi/pool -echo "$BACKEND" >/opt/localagi/.backend -echo "$COMPOSE_FILE" >/opt/localagi/.compose_file +msg_info "Configuring LocalAGI" +cat </opt/localagi/.env +LOCALAGI_MODEL=gemma-3-4b-it-qat +LOCALAGI_MULTIMODAL_MODEL=moondream2-20250414 +LOCALAGI_IMAGE_MODEL=sd-1.5-ggml +LOCALAGI_LLM_API_URL=http://127.0.0.1:8081 +LOCALAGI_STATE_DIR=/opt/localagi/pool +LOCALAGI_TIMEOUT=5m +LOCALAGI_ENABLE_CONVERSATIONS_LOGGING=false +LOCALAGI_GPU_BACKEND=${BACKEND} +EOF +msg_ok "Configured LocalAGI" -msg_info "Starting LocalAGI (${BACKEND})" -cd /opt/localagi || exit -if ! run_compose -f "$COMPOSE_FILE" pull; then - msg_error "Failed to pull LocalAGI images" +if ! build_localagi_source; then + msg_error "Failed to build LocalAGI from source" exit 1 fi -if ! run_compose -f "$COMPOSE_FILE" up -d; then - msg_error "Failed to start LocalAGI stack" + +msg_info "Creating Service" +cat </etc/systemd/system/localagi.service +[Unit] +Description=LocalAGI Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/localagi +EnvironmentFile=/opt/localagi/.env +ExecStart=/usr/local/bin/localagi +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl daemon-reload +systemctl enable -q --now localagi +msg_ok "Created Service" + +if ! systemctl is-active -q localagi; then + msg_error "Failed to start LocalAGI service" exit 1 fi msg_ok "Started LocalAGI (${BACKEND})"