Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2685a73897 | |||
| f5d020e39a |
@@ -24,7 +24,7 @@ cAIc splits the workload across two machine roles:
|
||||
|
||||
**Coordinator** (ultron — Ryzen 7 7840HS, no discrete GPU) runs the FastAPI app, RAG vector search (Qdrant), text embedding (Ollama on CPU), query triage (Phi-4-mini), web search (SearXNG), message broker (RabbitMQ), and all SQLite-backed services — memory, profiles, conversations, settings. Every CPU-bound task stays here.
|
||||
|
||||
**Workers** (jarvis — RX 6600 XT 12 GB / corsair — RTX 5070 Ti 16 GB) run only llama-server for GPU inference. The coordinator never touches a model; workers never touch the database. Workers register via AMQP, receive ping/pong health checks, and accept model-swap commands when triage determines a different model is needed for the current query.
|
||||
**Workers** (jarvis — RX 6600 XT 8 GB / corsair — RTX 5070 Ti 16 GB) run only llama-server for GPU inference. The coordinator never touches a model; workers never touch the database. Workers register via AMQP, receive ping/pong health checks, and accept model-swap commands when triage determines a different model is needed for the current query.
|
||||
|
||||
This split keeps the UI responsive during inference (the coordinator isn't blocked by GPU compute) and lets workers focus VRAM entirely on model weights rather than browser sessions or API orchestration.
|
||||
|
||||
@@ -526,6 +526,52 @@ Settings are stored in the `settings` table and include:
|
||||
- `skills_enabled` — Skills framework (true/false)
|
||||
- `default_model` — Default inference model
|
||||
|
||||
## Uninstalling cAIc
|
||||
|
||||
Three scripts are provided in `scripts/`. Each accepts `-y` for unattended execution.
|
||||
|
||||
### Bare-metal / systemd removal
|
||||
|
||||
Stop the service and remove `/opt/caic/`, the systemd unit, AMQP secret, and optionally the pip packages:
|
||||
|
||||
```bash
|
||||
sudo ./scripts/uninstall.sh # interactive
|
||||
sudo ./scripts/uninstall.sh -y # unattended
|
||||
```
|
||||
|
||||
Removes: systemd `caic` service, `/opt/caic/` + venv, `~/.caic_amqp_secret`, `/tmp/caic_uploads`, `hardware_state.json`. Preserves `caic.db` if it lives outside `/opt/caic/`.
|
||||
|
||||
### Docker stack teardown
|
||||
|
||||
Stop all containers, remove volumes/images, and delete generated files:
|
||||
|
||||
```bash
|
||||
cd <docker-deploy-directory>
|
||||
../scripts/teardown-docker.sh # interactive
|
||||
../scripts/teardown-docker.sh -y # unattended
|
||||
```
|
||||
|
||||
Removes: containers + volumes (`caic_data`, `caic_uploads`, `searxng_config`, `qdrant_storage`, `ollama_models`, `rabbitmq`), images (`caic`, `searxng`, `Qdrant`, `RabbitMQ`, `llama-server`, `Ollama`), `.env`, `secrets/`, `searxng/`, `setup.log`. Preserves `models/*.gguf` unless confirmed.
|
||||
|
||||
### Nuclear clean (everything)
|
||||
|
||||
Removes bare-metal install AND Docker stack AND config AND temp data. Double-confirmation required:
|
||||
|
||||
```bash
|
||||
sudo ./scripts/nuclear-clean.sh # double prompt, then unattended
|
||||
```
|
||||
|
||||
Removes: everything from the two scripts above plus `/var/lib/caic/` and temp directories. Offers to delete the repository itself. Does NOT remove Docker Engine, pip packages, GPU drivers, or WireGuard config.
|
||||
|
||||
### Partial / manual clean
|
||||
|
||||
Files and components not tracked by the scripts:
|
||||
|
||||
- `caic.db` (SQLite database at custom `CAIC_DB_PATH`)
|
||||
- Reverse proxy configs (Caddyfile, nginx)
|
||||
- WireGuard tunnel configurations
|
||||
- Docker Engine itself (`sudo apt remove docker containerd runc; sudo rm -rf /var/lib/docker`)
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
# cAIc — nuclear clean uninstall
|
||||
# Removes every trace of cAIc: bare-metal systemd install AND Docker stack.
|
||||
# Prompts for each step. Use -y to skip ALL prompts (fully automatic).
|
||||
# Use with extreme caution — will delete data and model files.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKIP=false
|
||||
[ "${1:-}" = "-y" ] && SKIP=true
|
||||
|
||||
prompt_yn() {
|
||||
if $SKIP; then return 0; fi
|
||||
local msg=$1; local default=${2:-n}
|
||||
if [[ "$default" == "y" ]]; then
|
||||
read -p "$msg [Y/n] " r; [[ -z "$r" || "$r" =~ ^[Yy] ]]
|
||||
else
|
||||
read -p "$msg [y/N] " r; [[ "$r" =~ ^[Yy] ]]
|
||||
fi
|
||||
}
|
||||
warn() { echo -e "${YELLOW}$1${NC}"; }
|
||||
|
||||
if ! $SKIP; then
|
||||
echo ""
|
||||
echo "${RED}================================================${NC}"
|
||||
echo "${RED} cAIc NUCLEAR CLEAN - removes all traces${NC}"
|
||||
echo "${RED} This will delete data, models, and the app.${NC}"
|
||||
echo "${RED}================================================${NC}"
|
||||
echo ""
|
||||
echo "cAIc NUCLEAR CLEAN"
|
||||
echo "=================="
|
||||
read -p "Type 'yes' to continue: " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
else
|
||||
SKIP=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! $SKIP; then
|
||||
prompt_yn "Run nuclear clean?" y || { echo "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========== PART 1: Docker stack =========="
|
||||
if command -v docker &>/dev/null; then
|
||||
# Stop all cAIc-related containers
|
||||
docker compose down 2>/dev/null || docker-compose down 2>/dev/null || echo " no compose stack running"
|
||||
docker ps --filter name=cai[c] --filter name=searxng --filter name=qdrant --filter name=rabbit --filter name=llama-server --filter name=ollama --filter name=caic -aq 2>/dev/null \
|
||||
| xargs -r docker rm -f 2>/dev/null || true
|
||||
docker volume rm caic_data caic_uploads searxng_config qdrant_storage rabbitmq_data ollama_models 2>/dev/null || echo " volumes already gone"
|
||||
docker rmi caic:latest 2>/dev/null || echo " caic image already gone"
|
||||
echo " Docker stack removed"
|
||||
else
|
||||
echo " Docker not installed — skipping"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========== PART 2: Systemd service ========="
|
||||
SERVICE_FILE="/etc/systemd/system/caic.service"
|
||||
if [ -f "$SERVICE_FILE" ]; then
|
||||
stop caic 2>/dev/null || true
|
||||
systemctl disable caic 2>/dev/null || true
|
||||
rm -f "$SERVICE_FILE"
|
||||
systemctl daemon-reload
|
||||
echo " systemd service removed"
|
||||
else
|
||||
echo " no systemd service found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========== PART 3: Install directory ========"
|
||||
for dir in /opt/caic /var/lib/caic; do
|
||||
if [ -d "$dir" ]; then
|
||||
rm -rf "$dir"
|
||||
echo " Removed: $dir"
|
||||
else
|
||||
echo " Skipped: $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========== PART 4: Config and state files ========"
|
||||
for f in \
|
||||
/home/gramps/.caic_amqp_secret \
|
||||
hardware_state.json \
|
||||
setup.log \
|
||||
.env; do
|
||||
rm -f "$f" 2>/dev/null || true
|
||||
done
|
||||
echo " Config/state files cleaned"
|
||||
|
||||
echo ""
|
||||
echo "========== PART 5: Temp data ==========="
|
||||
rm -rf /tmp/caic_uploads 2>/dev/null || true
|
||||
echo " Temp data cleaned"
|
||||
|
||||
echo ""
|
||||
echo "========== PART 6: User data (prompt each) ========="
|
||||
if prompt_yn "Remove the repository (current dir)?" n; then
|
||||
cd ..
|
||||
rm -rf "$SCRIPT_DIR"
|
||||
echo " Repository removed"
|
||||
else
|
||||
echo " Repository preserved"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "${GREEN}cAIc nuclear clean complete.${NC}"
|
||||
echo ""
|
||||
echo "Manually verify:"
|
||||
echo " - ls /opt/caic/"
|
||||
echo " - ls /etc/systemd/system/caic*"
|
||||
echo " - docker images | grep -E \"caic|searx|qdrant|rabbit|llama|ollama\""
|
||||
echo " - docker ps -a"
|
||||
echo ""
|
||||
echo "To remove Docker Engine itself:"
|
||||
echo " sudo apt remove docker.io containerd runc"
|
||||
echo " sudo rm -rf /var/lib/docker"
|
||||
echo ""
|
||||
echo "To remove python packages:"
|
||||
echo " pip uninstall fastapi uvicorn httpx psutil jinja2 python-multipart pypdf aio-pika"
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# cAIc — Docker stack teardown
|
||||
# Removes all containers, volumes, images, and generated files
|
||||
# created by setup-wizard / docker compose.
|
||||
# Run from the docker deployment directory. Use -y to skip prompts.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
prompt_yn() {
|
||||
if [ "${1:-}" = "-y" ]; then return 0; fi
|
||||
[ "$SKIP" = true ] && return 0
|
||||
local msg=$1; local default=${2:-n}
|
||||
if [[ "$default" == "y" ]]; then
|
||||
read -p "$msg [Y/n] " r; [[ -z "$r" || "$r" =~ ^[Yy] ]]
|
||||
else
|
||||
read -p "$msg [y/N] " r; [[ "$r" =~ ^[Yy] ]]
|
||||
fi
|
||||
}
|
||||
|
||||
SKIP=false
|
||||
[ "${1:-}" = "-y" ] && SKIP=true
|
||||
|
||||
echo "cAIc Docker stack teardown"
|
||||
echo "=========================="
|
||||
|
||||
# ---- Step 1: docker compose down -v ----
|
||||
if [ -f docker-compose.yml ] || [ -f compose.yml ]; then
|
||||
if prompt_yn "Stop Docker stack and remove volumes?" n; then
|
||||
docker compose down -v 2>/dev/null || docker-compose down -v 2>/dev/null || true
|
||||
echo " Docker stack stopped and volumes removed"
|
||||
else
|
||||
warn " Skipped: Docker stack left intact"
|
||||
fi
|
||||
else
|
||||
echo " Skipped: no compose file found"
|
||||
fi
|
||||
|
||||
# ---- Step 2: Remove Docker images ----
|
||||
if prompt_yn "Remove cAIc Docker image?" n; then
|
||||
docker rmi caic:latest 2>/dev/null || true
|
||||
echo " Removed caic:latest"
|
||||
|
||||
warn "Remove SearXNG / Qdrant / RabbitMQ / llama-server / Ollama images?"
|
||||
if prompt_yn " Remove service images?" n; then
|
||||
for tag in searxng/searxng qdrant/qdrant rabbitmq:4-management \
|
||||
ghcr.io/ggml-org/llama.cpp ollama/ollama; do
|
||||
docker rmi "$tag" 2>/dev/null || true
|
||||
done
|
||||
echo " Service images removed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 3: Volumes ----
|
||||
if prompt_yn "Remove remaining Docker volumes?" n; then
|
||||
for vol in caic_data caic_uploads searxng_config qdrant_storage rabbitmq_data ollama_models; do
|
||||
docker volume rm "$vol" 2>/dev/null || true
|
||||
done
|
||||
echo " Docker volumes removed"
|
||||
fi
|
||||
|
||||
# ---- Step 4: .env ----
|
||||
if [ -f .env ]; then
|
||||
if prompt_yn "Remove .env file?" n; then
|
||||
rm -f .env
|
||||
echo " .env removed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 5: generated directories ----
|
||||
for dir in secrets searxng; do
|
||||
if [ -d "$dir" ]; then
|
||||
if prompt_yn "Remove $dir/ directory?" n; then
|
||||
rm -rf "$dir"
|
||||
echo " $dir/ removed"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# ---- Step 6: generated files ----
|
||||
for f in setup.log docker-compose.yml compose.yml; do
|
||||
[ -f "$f" ] || continue
|
||||
if prompt_yn "Remove $f?" n; then
|
||||
rm -f "$f"
|
||||
echo " $f removed"
|
||||
fi
|
||||
done
|
||||
|
||||
# ---- Step 7: model files (with big warning) ----
|
||||
if [ -d models ]; then
|
||||
echo ""
|
||||
echo -e "${RED}WARNING: model files (*.gguf) can be gigabytes each.${NC}"
|
||||
if prompt_yn "Remove all model files from ./models/?" n; then
|
||||
rm -rf models
|
||||
echo " models/ removed"
|
||||
else
|
||||
echo " models/ preserved"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}cAIc Docker stack teardown complete.${NC}"
|
||||
echo "Preserved:"
|
||||
echo " - ./models/ (unless you accepted removal)"
|
||||
echo ""
|
||||
echo "Note: Docker Engine itself is not removed. To remove it:"
|
||||
echo " sudo apt-get remove docker docker-engine docker.io containerd runc"
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
# cAIc — bare-metal/systemd uninstall
|
||||
# Removes the cAIc install deployed via the README Fresh Install path.
|
||||
# Run as root or with sudo. Use -y to skip prompts.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
SKIP_PROMPT=false
|
||||
[ "${1:-}" = "-y" ] && SKIP_PROMPT=true
|
||||
|
||||
prompt_yn() {
|
||||
if $SKIP_PROMPT; then return 0; fi
|
||||
local msg=$1; local default=${2:-n}
|
||||
if [[ "$default" == "y" ]]; then
|
||||
read -p "$msg [Y/n] " r; [[ -z "$r" || "$r" =~ ^[Yy] ]]
|
||||
else
|
||||
read -p "$msg [y/N] " r; [[ "$r" =~ ^[Yy] ]]
|
||||
fi
|
||||
}
|
||||
warn() { echo -e "${YELLOW}$1${NC}"; }
|
||||
|
||||
echo "cAIc Bare-metal / systemd uninstall"
|
||||
echo "===================================="
|
||||
|
||||
# ---- Step 1: systemd service ----
|
||||
SERVICE_FILE="/etc/systemd/system/caic.service"
|
||||
if [ -f "$SERVICE_FILE" ]; then
|
||||
warn "Stopping and disabling caic service..."
|
||||
systemctl stop caic 2>/dev/null || true
|
||||
systemctl disable caic 2>/dev/null || true
|
||||
if prompt_yn "Remove caic systemd service?" n; then
|
||||
rm -f "$SERVICE_FILE" /etc/systemd/system/caic.service
|
||||
systemctl daemon-reload
|
||||
echo " Removed systemd service"
|
||||
fi
|
||||
else
|
||||
echo " Skipped: no systemd service at $SERVICE_FILE"
|
||||
fi
|
||||
|
||||
# ---- Step 2: /opt/caic directory ----
|
||||
CAIC_DIR="/opt/caic"
|
||||
if [ -d "$CAIC_DIR" ]; then
|
||||
if prompt_yn "Remove cAIc installation directory ($CAIC_DIR)?" n; then
|
||||
rm -rf "$CAIC_DIR"
|
||||
echo " Removed $CAIC_DIR"
|
||||
else
|
||||
warn " Skipped: $CAIC_DIR preserved"
|
||||
fi
|
||||
else
|
||||
echo " Skipped: $CAIC_DIR does not exist"
|
||||
fi
|
||||
|
||||
# ---- Step 3: AMQP secret file ----
|
||||
if [ -f "/home/gramps/.caic_amqp_secret" ]; then
|
||||
if prompt_yn "Remove AMQP secret file (/home/gramps/.caic_amqp_secret)?" n; then
|
||||
rm -f /home/gramps/.caic_amqp_secret
|
||||
echo " Removed AMQP secret"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 4: Upload temp directory ----
|
||||
UPLOAD_DIR="/tmp/caic_uploads"
|
||||
if [ -d "$UPLOAD_DIR" ]; then
|
||||
if prompt_yn "Remove upload temp directory ($UPLOAD_DIR)?" n; then
|
||||
rm -rf "$UPLOAD_DIR"
|
||||
echo " Removed upload temp"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 5: hardware_state.json in cwd ----
|
||||
if [ -f "hardware_state.json" ]; then
|
||||
if prompt_yn "Remove cached hardware state?" n; then
|
||||
rm -f hardware_state.json
|
||||
echo " Removed cached hardware state"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 6: pip packages if user wants ----
|
||||
if command -v pip &>/dev/null; then
|
||||
if prompt_yn "Attempt to uninstall cAIc-related pip packages?" n; then
|
||||
pip uninstall -y fastapi uvicorn httpx psutil aio-pika jinja2 python-multipart pypdf 2>/dev/null || true
|
||||
echo " Uninstalled pip packages"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}cAIc bare-metal/uninstall complete.${NC}"
|
||||
echo "The following may remain:"
|
||||
echo " - /opt/caic/ (if you chose to preserve)"
|
||||
echo " - ~/.caic_amqp_secret (if preserved)"
|
||||
echo " - hardware_state.json (if preserved)"
|
||||
echo " - installed pip packages (if you chose to skip)"
|
||||
echo " - Python venv at /opt/caic/venv (removed with /opt/caic)"
|
||||
echo ""
|
||||
echo "Caic data stored outside install:"
|
||||
echo " - caic.db (if configured via CAIC_DB_PATH)"
|
||||
echo " - claude/opencode .jsonc updates (not tracked by this script)"
|
||||
@@ -227,8 +227,8 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(
|
||||
|
||||
.chat-container { flex:1; overflow-y:auto; padding:20px 30px; display:flex; flex-direction:column; gap:8px; background-image:radial-gradient(rgba(255,255,255,0.015) 1px, transparent 1px); background-size:24px 24px; margin-right:28px; }
|
||||
.msg-pair { display:flex; flex-direction:column; gap:8px; border-radius:var(--radius); padding:12px 12px 8px; border-left:3px solid transparent; }
|
||||
.msg-pair:nth-child(odd) { background:rgba(255,255,255,0.015); border-left-color:rgba(0,136,187,0.25); }
|
||||
.msg-pair:nth-child(even) { background:rgba(255,255,255,0.035); border-left-color:rgba(243,156,18,0.25); }
|
||||
.msg-pair:nth-child(odd) { background:rgba(255,255,255,0.015); border-left-color:rgba(0,136,187,0.30); }
|
||||
.msg-pair:nth-child(even) { background:rgba(255,255,255,0.025); border-left-color:rgba(0,136,187,0.12); }
|
||||
.welcome-screen { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; color:var(--text-muted); text-align:center; gap:12px; }
|
||||
.welcome-screen .logo { font-family:var(--font-mono); font-size:48px; color:var(--accent-dim); opacity:0.5; }
|
||||
.welcome-screen .welcome-logo { max-width:70%; height:auto; object-fit:contain; opacity:0.85; }
|
||||
@@ -329,7 +329,10 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(
|
||||
|
||||
|
||||
|
||||
.message.assistant.search-result { }
|
||||
.message.assistant.search-result .content { background:rgba(243,156,18,0.08); border:1px solid rgba(243,156,18,0.2); border-radius:var(--radius); padding:12px; }
|
||||
.message.assistant.search-result .role-label { color:var(--warning) !important; }
|
||||
.message.assistant.search-result .content .text { color:var(--warning); }
|
||||
.raw-results { margin-top:12px; background:var(--bg-tertiary); border:1px solid var(--border); border-radius:var(--radius); padding:8px 12px; font-size:12px; }
|
||||
.raw-results summary { cursor:pointer; color:var(--accent); font-family:var(--font-mono); }
|
||||
.raw-results ul { margin:8px 0 0 0; padding-left:20px; list-style:none; }
|
||||
@@ -1652,7 +1655,7 @@ async function sendSearch() {
|
||||
if (data.done) {
|
||||
const roleLabel = assistantDiv.querySelector('.role-label');
|
||||
if (roleLabel) {
|
||||
roleLabel.innerHTML += '<span class="search-badge-inline">🔍 web</span>';
|
||||
roleLabel.textContent = 'web search';
|
||||
if (ttr > 0) {
|
||||
const ttrSec = ttr / 1000;
|
||||
roleLabel.innerHTML += `<span class="ttr-badge search">ttr: ${ttrSec.toFixed(1)}s</span>`;
|
||||
@@ -1843,7 +1846,7 @@ async function sendMessage() {
|
||||
if (data.token) { if (firstToken) { textEl.innerHTML = ''; firstToken = false; ttr = performance.now() - ttrStart; tokenCount = 0; } fullText += data.token; tokenCount++; textEl.innerHTML = renderMarkdown(fullText); scrollToLatest(); }
|
||||
if (data.done) {
|
||||
const roleLabel = assistantDiv.querySelector('.role-label');
|
||||
if (data.searched && roleLabel) roleLabel.innerHTML += '<span class="search-badge-inline">🔍 web</span>';
|
||||
if (data.searched && roleLabel) roleLabel.textContent = 'web search';
|
||||
if (typeof data.perplexity === 'number' && roleLabel) { const ppl = data.perplexity; const conf = Math.round(Math.max(0, Math.min(100, (1 - (ppl - 1) / 10) * 100))); const cls = conf >= 80 ? 'high' : conf >= 15 ? 'medium' : 'low'; roleLabel.innerHTML += `<span class="conf-badge ${cls}">${conf}%</span>`; }
|
||||
if (typeof data.tokens_per_sec === 'number' && data.tokens_per_sec > 0 && roleLabel) roleLabel.innerHTML += `<span class="tps-badge">${data.tokens_per_sec.toFixed(1)} t/s</span>`;
|
||||
if (roleLabel && ttr > 0) {
|
||||
|
||||
Reference in New Issue
Block a user