9e8f1bbeed
Dashboard / frontend (push) Failing after 0s
Dashboard / api (push) Failing after 0s
Lint PowerShell / powershell-lint (ubuntu-latest) (push) Failing after 1s
Python Lint / Lint Python with Ruff (push) Failing after 1s
ShellCheck / Lint shell scripts (push) Failing after 1s
Matrix Smoke / linux-smoke (push) Failing after 1s
Matrix Smoke / distro: cachyos (push) Failing after 15s
Matrix Smoke / distro: linux-mint-21.3 (push) Failing after 15s
Matrix Smoke / distro: debian-12 (push) Failing after 5m21s
Matrix Smoke / distro: fedora-41 (push) Failing after 4m56s
Matrix Smoke / distro: ubuntu-24.04 (push) Failing after 2m13s
Matrix Smoke / distro: rocky-9 (push) Failing after 10m39s
Matrix Smoke / distro: manjaro (push) Failing after 12m11s
Matrix Smoke / distro: opensuse-tw (push) Failing after 11m53s
Matrix Smoke / distro: archlinux (push) Failing after 20m3s
Matrix Smoke / distro: ubuntu-22.04 (push) Failing after 13m49s
Validate .env Schema / tier-1-env-validation (push) Successful in 52s
Validate .env Schema / tier-2-env-validation (push) Successful in 44s
Validate .env Schema / tier-3-env-validation (push) Successful in 52s
Validate .env Schema / tier-4-env-validation (push) Successful in 51s
Validate Extensions Catalog / Check catalog is up-to-date (push) Failing after 9m47s
Secret Scan / Scan for secrets (push) Failing after 21m4s
Validate Docker Compose / Validate Docker Compose files (push) Has been cancelled
Python Type Check / Type check with mypy (push) Has been cancelled
Validate .env Schema / tier-0-env-validation (push) Has been cancelled
Test Linux / integration-smoke (push) Has been cancelled
Lint PowerShell / powershell-lint (windows-latest) (push) Has been cancelled
Matrix Smoke / macos-smoke (push) Has been cancelled
303 lines
9.3 KiB
Bash
Executable File
303 lines
9.3 KiB
Bash
Executable File
#!/bin/bash
|
||
# ODS First Boot Demo
|
||
# Shows off what your local AI stack can do in under 2 minutes
|
||
#
|
||
# Usage: ./first-boot-demo.sh [--all] [--quick]
|
||
# Mission: M5 (Clonable ODS Setup Server)
|
||
|
||
set -euo pipefail
|
||
|
||
#=============================================================================
|
||
# Colors
|
||
#=============================================================================
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
MAGENTA='\033[0;35m'
|
||
NC='\033[0m'
|
||
BOLD='\033[1m'
|
||
|
||
#=============================================================================
|
||
# Config — resolve from service registry when available
|
||
#=============================================================================
|
||
_DEMO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
if [[ -f "$_DEMO_DIR/lib/service-registry.sh" ]]; then
|
||
export SCRIPT_DIR="$_DEMO_DIR"
|
||
. "$_DEMO_DIR/lib/service-registry.sh"
|
||
sr_load
|
||
[[ -f "$_DEMO_DIR/lib/safe-env.sh" ]] && . "$_DEMO_DIR/lib/safe-env.sh"
|
||
load_env_file "$_DEMO_DIR/.env"
|
||
sr_resolve_ports
|
||
fi
|
||
|
||
LLM_URL="${LLM_URL:-http://localhost:${SERVICE_PORTS[llama-server]:-11434}}"
|
||
WHISPER_URL="${WHISPER_URL:-http://localhost:${SERVICE_PORTS[whisper]:-9000}}"
|
||
PIPER_URL="${PIPER_URL:-http://localhost:${SERVICE_PORTS[tts]:-8880}}"
|
||
N8N_URL="${N8N_URL:-http://localhost:${SERVICE_PORTS[n8n]:-5678}}"
|
||
WEBUI_URL="${WEBUI_URL:-http://localhost:${SERVICE_PORTS[open-webui]:-3000}}"
|
||
|
||
QUICK_MODE=false
|
||
ALL_MODE=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--quick) QUICK_MODE=true; shift ;;
|
||
--all) ALL_MODE=true; shift ;;
|
||
-h|--help)
|
||
echo "Usage: $0 [--quick] [--all]"
|
||
echo " --quick Skip slow demos, just show what's available"
|
||
echo " --all Run all demos including voice (requires audio files)"
|
||
exit 0
|
||
;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
|
||
#=============================================================================
|
||
# Helpers
|
||
#=============================================================================
|
||
header() {
|
||
echo ""
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${BOLD}${CYAN} $1${NC}"
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
}
|
||
|
||
demo() {
|
||
echo -e "\n${MAGENTA}▶${NC} ${BOLD}$1${NC}"
|
||
}
|
||
|
||
success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
fail() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
}
|
||
|
||
info() {
|
||
echo -e "${CYAN}ℹ${NC} $1"
|
||
}
|
||
|
||
wait_key() {
|
||
if [[ "$QUICK_MODE" != "true" ]]; then
|
||
echo -e "\n${YELLOW}Press Enter to continue...${NC}"
|
||
read -r
|
||
fi
|
||
}
|
||
|
||
check_service() {
|
||
local name=$1
|
||
local url=$2
|
||
local endpoint=${3:-/health}
|
||
|
||
if curl -sf "${url}${endpoint}" > /dev/null 2>&1; then
|
||
success "$name is running at $url"
|
||
return 0
|
||
else
|
||
fail "$name not responding at $url"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
#=============================================================================
|
||
# Welcome
|
||
#=============================================================================
|
||
clear
|
||
echo ""
|
||
echo -e "${BOLD}${CYAN}"
|
||
cat << 'EOF'
|
||
OOOOO DDDD SSSSS
|
||
OO OO DD DD SS
|
||
OO OO DD DD SSS
|
||
OO OO DD DD SS
|
||
OOOOO DDDD SSSS
|
||
|
||
EOF
|
||
echo -e "${NC}"
|
||
echo -e "${BOLD}ODS - First Boot Demo${NC}"
|
||
echo -e "Everything runs on YOUR hardware. No cloud. No API costs. Full privacy.\n"
|
||
|
||
#=============================================================================
|
||
# Health Check
|
||
#=============================================================================
|
||
header "🔍 Checking Services"
|
||
|
||
SERVICES_OK=0
|
||
SERVICES_TOTAL=0
|
||
|
||
# Core services
|
||
((SERVICES_TOTAL++)) || true
|
||
if check_service "LLM (llama-server)" "$LLM_URL" "/health"; then
|
||
((SERVICES_OK++)) || true
|
||
LLM_AVAILABLE=true
|
||
else
|
||
LLM_AVAILABLE=false
|
||
fi
|
||
|
||
((SERVICES_TOTAL++)) || true
|
||
if check_service "Open WebUI" "$WEBUI_URL" "/"; then
|
||
((SERVICES_OK++)) || true
|
||
fi
|
||
|
||
# Optional services
|
||
if curl -sf "${WHISPER_URL}/health" > /dev/null 2>&1; then
|
||
success "Whisper STT is running (voice input enabled)"
|
||
WHISPER_AVAILABLE=true
|
||
((SERVICES_OK++)) || true
|
||
((SERVICES_TOTAL++)) || true
|
||
else
|
||
info "Whisper STT not running (voice input disabled)"
|
||
WHISPER_AVAILABLE=false
|
||
fi
|
||
|
||
if curl -sf "${PIPER_URL}" > /dev/null 2>&1; then
|
||
success "OpenTTS TTS is running (voice output enabled)"
|
||
PIPER_AVAILABLE=true
|
||
((SERVICES_OK++)) || true
|
||
((SERVICES_TOTAL++)) || true
|
||
else
|
||
info "OpenTTS TTS not running (voice output disabled)"
|
||
PIPER_AVAILABLE=false
|
||
fi
|
||
|
||
if curl -sf "${N8N_URL}/healthz" > /dev/null 2>&1; then
|
||
success "n8n Workflows is running (automation enabled)"
|
||
N8N_AVAILABLE=true
|
||
((SERVICES_OK++)) || true
|
||
((SERVICES_TOTAL++)) || true
|
||
else
|
||
info "n8n not running (automation disabled)"
|
||
N8N_AVAILABLE=false
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${BOLD}Services: ${SERVICES_OK}/${SERVICES_TOTAL} running${NC}"
|
||
|
||
if [[ "$LLM_AVAILABLE" != "true" ]]; then
|
||
echo -e "\n${RED}LLM (llama-server) is required for demos. Is it still loading?${NC}"
|
||
echo "Check status: docker compose logs -f llama-server"
|
||
exit 1
|
||
fi
|
||
|
||
wait_key
|
||
|
||
#=============================================================================
|
||
# Demo 1: Chat Completion
|
||
#=============================================================================
|
||
header "💬 Demo 1: Local Chat Completion"
|
||
|
||
demo "Asking your local AI a question..."
|
||
|
||
RESPONSE=$(curl -sf "${LLM_URL}/v1/chat/completions" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"model": "Qwen/Qwen2.5-32B-Instruct-AWQ",
|
||
"messages": [{"role": "user", "content": "In one sentence, what makes local AI special?"}],
|
||
"max_tokens": 100,
|
||
"temperature": 0.7
|
||
}' 2>/dev/null | jq -r '.choices[0].message.content' 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$RESPONSE" && "$RESPONSE" != "null" ]]; then
|
||
echo ""
|
||
echo -e "${GREEN}Response:${NC}"
|
||
echo -e " ${RESPONSE}"
|
||
echo ""
|
||
success "Local LLM responded! No API calls, no cloud, just your GPU."
|
||
else
|
||
fail "No response from LLM"
|
||
fi
|
||
|
||
wait_key
|
||
|
||
#=============================================================================
|
||
# Demo 2: Code Assistance
|
||
#=============================================================================
|
||
header "🧑💻 Demo 2: Code Assistance"
|
||
|
||
demo "Asking for help with a Python function..."
|
||
|
||
CODE_RESPONSE=$(curl -sf "${LLM_URL}/v1/chat/completions" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"model": "Qwen/Qwen2.5-32B-Instruct-AWQ",
|
||
"messages": [{"role": "user", "content": "Write a Python one-liner to reverse a string. Just the code, no explanation."}],
|
||
"max_tokens": 50,
|
||
"temperature": 0.3
|
||
}' 2>/dev/null | jq -r '.choices[0].message.content' 2>/dev/null || echo "")
|
||
|
||
if [[ -n "$CODE_RESPONSE" && "$CODE_RESPONSE" != "null" ]]; then
|
||
echo ""
|
||
echo -e "${GREEN}Generated code:${NC}"
|
||
echo -e " ${CODE_RESPONSE}"
|
||
echo ""
|
||
success "Code assistant works! Great for development."
|
||
else
|
||
fail "No response from code assistant"
|
||
fi
|
||
|
||
wait_key
|
||
|
||
#=============================================================================
|
||
# Demo 3: Streaming
|
||
#=============================================================================
|
||
header "📡 Demo 3: Streaming Response"
|
||
|
||
demo "Watching tokens stream in real-time..."
|
||
echo ""
|
||
|
||
# Simple streaming demo - just show it works
|
||
curl -sN "${LLM_URL}/v1/chat/completions" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"model": "Qwen/Qwen2.5-32B-Instruct-AWQ",
|
||
"messages": [{"role": "user", "content": "Count from 1 to 5, one number per line."}],
|
||
"max_tokens": 50,
|
||
"temperature": 0,
|
||
"stream": true
|
||
}' 2>/dev/null | while read -r line; do
|
||
if [[ "$line" == data:* ]]; then
|
||
content=$(echo "${line#data: }" | jq -r '.choices[0].delta.content // empty' 2>/dev/null)
|
||
if [[ -n "$content" ]]; then
|
||
printf "%s" "$content"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
echo ""
|
||
success "Streaming works! Great for real-time UIs."
|
||
|
||
wait_key
|
||
|
||
#=============================================================================
|
||
# Summary
|
||
#=============================================================================
|
||
header "🎉 Demo Complete!"
|
||
|
||
echo ""
|
||
echo -e "${BOLD}What you just saw:${NC}"
|
||
echo " ✓ Local LLM responding to prompts"
|
||
echo " ✓ Code assistance capabilities"
|
||
echo " ✓ Real-time streaming"
|
||
echo ""
|
||
|
||
echo -e "${BOLD}What's available:${NC}"
|
||
echo " • Open WebUI: ${WEBUI_URL}"
|
||
[[ "$N8N_AVAILABLE" == "true" ]] && echo " • n8n Workflows: ${N8N_URL}"
|
||
[[ "$WHISPER_AVAILABLE" == "true" ]] && echo " • Whisper STT: ${WHISPER_URL}"
|
||
[[ "$PIPER_AVAILABLE" == "true" ]] && echo " • OpenTTS TTS: ${PIPER_URL}"
|
||
echo ""
|
||
|
||
echo -e "${BOLD}Next steps:${NC}"
|
||
echo " 1. Open ${WEBUI_URL} and start chatting"
|
||
echo " 2. Import workflows from ./workflows/ into n8n"
|
||
echo " 3. Try the voice demo: ./scripts/voice-demo.sh"
|
||
echo " 4. OpenClaw agent: http://localhost:7860"
|
||
echo ""
|
||
|
||
echo -e "${CYAN}Everything runs locally. Your data stays private. Enjoy! 🚀${NC}"
|
||
echo ""
|