#!/bin/bash # ╔══════════════════════════════════════════════════════════════════╗ # ║ DROID FACTORY CLI v2.1 — Sovereign AI with Local Fallback ║ # ║ Chain: Cerebras 235B → Groq 70B → Ollama Local (unlimited) ║ # ║ 23 cognitive domains · 22 modules · 27 KBs · Rate-limited ║ # ╚══════════════════════════════════════════════════════════════════╝ DROID_VERSION="2.1.0" BRAIN_HOME="/opt/wevia-brain" WEVIA_IA="/var/www/weval/wevia-ia" LOG="/var/log/droid-cli.log" RATE_FILE="/tmp/droid-rate-state" # ═══ PROVIDERS ═══ CEREBRAS_URL="https://api.cerebras.ai/v1/chat/completions" CEREBRAS_KEY="csk-4wrrhkpr568ry9xx49k9mcynwdx483nx53dd62yh5xedfckh" CEREBRAS_MODEL="qwen-3-235b-a22b-instruct-2507" CEREBRAS_RPM=30 GROQ_URL="https://api.groq.com/openai/v1/chat/completions" GROQ_KEY="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" GROQ_MODEL="llama-3.3-70b-versatile" GROQ_RPM=30 OLLAMA_URL="http://localhost:11434/api/chat" OLLAMA_MODEL="llama3.1:8b" R='\033[0;31m'; G='\033[0;32m'; Y='\033[1;33m'; B='\033[0;34m'; C='\033[0;36m'; M='\033[0;35m'; W='\033[0m'; BOLD='\033[1m' log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG"; } # ═══ RATE LIMITER ═══ rate_check() { local provider=$1 rpm=$2 local sf="${RATE_FILE}_${provider}" local now=$(date +%s) ws=0 count=0 [ -f "$sf" ] && read ws count < "$sf" local elapsed=$((now - ws)) if [ $elapsed -ge 60 ]; then echo "$now 1" > "$sf"; return 0; fi if [ $count -ge $rpm ]; then local wait=$((60 - elapsed)) echo -e "${Y}⏳ Rate limit ($provider: $count/$rpm). Wait ${wait}s...${W}" >&2 sleep $wait echo "$(date +%s) 1" > "$sf"; return 0 fi echo "$ws $((count + 1))" > "$sf" } # ═══ OLLAMA LOCAL CALL (unlimited) ═══ ollama_call() { local prompt="$1" model="${2:-$OLLAMA_MODEL}" local escaped=$(echo "$prompt" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read())[1:-1])") local resp=$(curl -s --max-time 120 "$OLLAMA_URL" \ -d "{\"model\":\"$model\",\"messages\":[{\"role\":\"user\",\"content\":\"$escaped\"}],\"stream\":false}") echo "$resp" | python3 -c " import sys,json try: d=json.load(sys.stdin) print(d.get('message',{}).get('content','ERROR: no content')) except: print('ERROR: ollama parse failed')" 2>/dev/null } # ═══ CLOUD AI CALL ═══ cloud_call() { local prompt="$1" provider="$2" maxtok="${3:-2048}" local url key model case $provider in cerebras) url="$CEREBRAS_URL"; key="$CEREBRAS_KEY"; model="$CEREBRAS_MODEL"; rate_check cerebras $CEREBRAS_RPM ;; groq) url="$GROQ_URL"; key="$GROQ_KEY"; model="$GROQ_MODEL"; rate_check groq $GROQ_RPM ;; esac local ep=$(echo "$prompt" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read())[1:-1])") local resp=$(curl -s --max-time 30 "$url" -H "Authorization: Bearer $key" -H "Content-Type: application/json" \ -d "{\"model\":\"$model\",\"max_tokens\":$maxtok,\"messages\":[{\"role\":\"user\",\"content\":\"$ep\"}]}") echo "$resp" | python3 -c " import sys,json try: d=json.load(sys.stdin) if 'choices' in d: print(d['choices'][0]['message']['content']) elif 'error' in d: print(f'ERROR: {d[\"error\"].get(\"message\",\"?\")}') else: print('ERROR: unexpected') except: print('ERROR: parse')" 2>/dev/null } # ═══ AI CALL WITH CHAIN FAILOVER ═══ # Cerebras 235B → Groq 70B → Ollama Local (unlimited) ai_call() { local prompt="$1" maxtok="${2:-2048}" # Try Cerebras first local out=$(cloud_call "$prompt" "cerebras" "$maxtok") if [ -n "$out" ] && [[ "$out" != ERROR:* ]]; then log "AI cerebras OK" echo "$out"; return 0 fi echo -e "${Y}⚡ Cerebras failed → Groq${W}" >&2 log "FAILOVER cerebras→groq: $out" # Try Groq out=$(cloud_call "$prompt" "groq" "$maxtok") if [ -n "$out" ] && [[ "$out" != ERROR:* ]]; then log "AI groq OK" echo "$out"; return 0 fi echo -e "${M}🏠 Cloud failed → Ollama Local (unlimited)${W}" >&2 log "FAILOVER groq→ollama: $out" # Ollama local — unlimited, no rate limit out=$(ollama_call "$prompt" "$OLLAMA_MODEL") if [ -n "$out" ] && [[ "$out" != ERROR:* ]]; then log "AI ollama OK model=$OLLAMA_MODEL" echo "$out"; return 0 fi # Last resort: try tinyllama (fastest local) echo -e "${R}⚠ llama3.1 failed → tinyllama${W}" >&2 out=$(ollama_call "$prompt" "tinyllama") log "AI tinyllama fallback" echo "$out" } # ═══ COMMANDS ═══ cmd_status() { echo -e "${BOLD}╔══ DROID CLI v${DROID_VERSION} ══╗${W}" echo -e "\n${B}▸ Providers (chain: Cerebras→Groq→Ollama):${W}" for p in cerebras groq; do local sf="${RATE_FILE}_${p}" c=0 rpm=0 [ "$p" = "cerebras" ] && rpm=$CEREBRAS_RPM || rpm=$GROQ_RPM [ -f "$sf" ] && read _ c < "$sf" echo -e " ${G}●${W} $p: $c/$rpm RPM" done # Ollama status local ollama_ok=$(curl -s --max-time 3 http://localhost:11434/api/tags 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('models',[])))" 2>/dev/null) if [ -n "$ollama_ok" ] && [ "$ollama_ok" -gt 0 ] 2>/dev/null; then echo -e " ${G}●${W} ollama: ${ollama_ok} models (${M}UNLIMITED${W})" else echo -e " ${R}✗${W} ollama: offline" fi echo -e "\n${B}▸ Brain:${W}" local mods=$(find "$BRAIN_HOME/modules" -name "*.php" 2>/dev/null | wc -l) local cogs=$(find "$BRAIN_HOME/cognitive" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) local kbs=$(find "$BRAIN_HOME/knowledge" -type f 2>/dev/null | wc -l) local prm=$(find "$BRAIN_HOME/prompts" -type f 2>/dev/null | wc -l) local s89=$(find "$BRAIN_HOME/s89-ai-apis" -name "*.php" 2>/dev/null | wc -l) echo -e " Modules:${G}$mods${W} Cognitive:${G}$cogs${W} KBs:${G}$kbs${W} Prompts:${G}$prm${W} S89:${G}$s89${W}" echo -e "\n${B}▸ Disk:${W}" df -h / | tail -1 | awk '{printf " %s/%s (%s)\n",$3,$2,$5}' echo -e "\n${B}▸ Services:${W}" for svc in apache2 postgresql; do systemctl is-active --quiet $svc 2>/dev/null && echo -e " ${G}●${W} $svc" || echo -e " ${R}✗${W} $svc" done echo -e "${BOLD}╚════════════════════════╝${W}" } cmd_ask() { local q="$*" [ -z "$q" ] && { echo "Usage: droid ask "; return 1; } local ctx="" echo "$q" | grep -qi "code\|debug\|php\|python\|bash" && ctx="Context: code-gen, debug, self-correction modules loaded." echo "$q" | grep -qi "architect\|plan\|strategy" && ctx="Context: strategy, orchestration, decisions modules loaded." echo "$q" | grep -qi "data\|analys\|report" && ctx="Context: analysis, data-storytelling, math modules loaded." local sys="You are DROID v${DROID_VERSION}, sovereign AI agent for WEVAL Consulting (S95). You have 23 cognitive domains + 22 brain modules + 27 KBs in /opt/wevia-brain. $ctx Be concise and precise." echo -e "${C}🤖 Processing (chain: 235B→70B→local)...${W}" ai_call "$sys\n\nUser: $q" 2048 } cmd_analyze() { local t="$1" [ -z "$t" ] && { echo "Usage: droid analyze "; return 1; } [ ! -e "$t" ] && { echo "Not found: $t"; return 1; } local content="" if [ -d "$t" ]; then content=$(find "$t" -name "*.php" -o -name "*.js" -o -name "*.py" -o -name "*.sh" | head -20 | while read f; do echo "=== $f ($(wc -l < "$f")L) ==="; head -10 "$f"; done) else content=$(head -100 "$t") fi echo -e "${C}🔍 Analyzing $t...${W}" ai_call "Analyze this code. Purpose, Issues, Improvements, Security. Concise.\n\n$content" 2048 } cmd_fix() { local f="$1" issue="$2" [ -z "$f" ] && { echo "Usage: droid fix [issue]"; return 1; } [ ! -f "$f" ] && { echo "Not found: $f"; return 1; } cp "$f" "${f}.GOLD-$(date +%Y%m%d-%H%M%S)-pre-fix" local content=$(cat "$f") echo -e "${C}🔧 Fixing $f...${W}" local fixed=$(ai_call "Fix this file. Issue: ${issue:-auto-detect}. Return ONLY fixed code.\n\nFile: $f\n\n$content" 4096) if [ -n "$fixed" ] && [[ "$fixed" != ERROR:* ]]; then echo "$fixed" > "$f" echo -e "${G}✓ Fixed. Backup: ${f}.GOLD-*${W}" else echo -e "${R}✗ Fix failed: $fixed${W}" fi } cmd_scan() { echo -e "${BOLD}╔══ Security Scan ══╗${W}" echo -e "\n${B}▸ Open ports:${W}" ss -tlnp 2>/dev/null | grep LISTEN | awk '{print " "$4" → "$6}' | head -15 echo -e "\n${B}▸ Apache auth:${W}" for conf in /etc/apache2/sites-enabled/*.conf; do local n=$(basename "$conf" .conf) grep -q "AuthType" "$conf" 2>/dev/null && echo -e " ${G}🔒${W} $n" || echo -e " ${Y}⚠${W} $n" done echo -e "\n${B}▸ Disk hotspots:${W}" du -sh /var/www/* /opt/* 2>/dev/null | sort -rh | head -5 echo -e "${BOLD}╚══════════════════╝${W}" } cmd_nonreg() { local s="${1:-all}" echo -e "${BOLD}Running nonreg: $s${W}" case $s in ethica) [ -f /opt/wevads/scripts/ethica-nonreg.sh ] && bash /opt/wevads/scripts/ethica-nonreg.sh ;; wevia) [ -f /opt/wevads-v2/nonreg-all.sh ] && bash /opt/wevads-v2/nonreg-all.sh ;; all) for sc in /opt/wevads/scripts/ethica-nonreg.sh /opt/wevads-v2/nonreg-all.sh; do [ -f "$sc" ] && bash "$sc" 2>&1 | tail -5; done ;; esac } cmd_brain() { case "${1:-list}" in list) echo -e "${BOLD}╔══ Brain Map ══╗${W}" echo -e "\n${B}▸ Cognitive (23):${W}" ls "$BRAIN_HOME/cognitive/" 2>/dev/null | while read d; do echo -e " ${C}◆${W} $d ($(find "$BRAIN_HOME/cognitive/$d" -type f 2>/dev/null | wc -l))" done echo -e "\n${B}▸ Modules:${W}" for cat in agentic core pipeline advanced v2; do local fl=$(ls "$BRAIN_HOME/modules/$cat/" 2>/dev/null | tr '\n' ', ') [ -n "$fl" ] && echo -e " ${G}▸${W} $cat: $fl" done echo -e "\n${B}▸ KBs:${W}" find "$BRAIN_HOME/knowledge" -type f 2>/dev/null | while read f; do echo -e " ${B}📚${W} $(basename $f)"; done echo -e "${BOLD}╚══════════════╝${W}" ;; activate) [ -d "$BRAIN_HOME/cognitive/$2" ] && echo -e "${C}🧠 Loaded cognitive/$2${W}" || echo -e "${R}✗ Not found: $2${W}" ;; esac } cmd_deploy() { local src="$1" dest="${2:-/var/www/html/}" [ -z "$src" ] && { echo "Usage: droid deploy [dest]"; return 1; } [ -e "$dest/$(basename $src)" ] && cp "$dest/$(basename $src)" "$dest/$(basename $src).GOLD-$(date +%Y%m%d-%H%M%S)" cp -r "$src" "$dest/" && echo -e "${G}✓ Deployed $(basename $src) → $dest${W}" log "DEPLOY $src → $dest" } cmd_rate() { echo -e "${BOLD}▸ Rate Limits:${W}" for p in cerebras groq; do local sf="${RATE_FILE}_${p}" rpm=0 c=0 [ "$p" = "cerebras" ] && rpm=$CEREBRAS_RPM || rpm=$GROQ_RPM if [ -f "$sf" ]; then read ts c < "$sf" local r=$((60 - $(date +%s) + ts)); [ $r -lt 0 ] && r=0 && c=0 echo -e " $p: ${G}$c${W}/$rpm | reset ${r}s" else echo -e " $p: ${G}0${W}/$rpm" fi done echo -e " ollama: ${M}UNLIMITED${W} (local CPU)" } cmd_local() { local q="$*" [ -z "$q" ] && { echo "Usage: droid local — Forces Ollama (no cloud)"; return 1; } echo -e "${M}🏠 Ollama local ($OLLAMA_MODEL, unlimited)...${W}" ollama_call "You are DROID, sovereign AI agent. Be concise.\n\nUser: $q" "$OLLAMA_MODEL" } cmd_help() { echo -e "${BOLD}DROID CLI v${DROID_VERSION} — Chain: Cerebras 235B → Groq 70B → Ollama Local${W}" echo -e " ${G}droid status${W} System + provider chain status" echo -e " ${G}droid ask${W} AI Q&A (auto-failover chain)" echo -e " ${G}droid local${W} Force Ollama local (unlimited)" echo -e " ${G}droid analyze${W} AI code analysis" echo -e " ${G}droid fix${W} AI auto-fix + GOLD backup" echo -e " ${G}droid scan${W} Security scan" echo -e " ${G}droid nonreg${W} [suite] Non-regression tests" echo -e " ${G}droid brain${W} [list|activate ]" echo -e " ${G}droid deploy${W} [d] Deploy with backup" echo -e " ${G}droid rate${W} Rate limit status" } case "${1:-help}" in status) cmd_status ;; ask) shift; cmd_ask "$@" ;; local) shift; cmd_local "$@" ;; analyze) shift; cmd_analyze "$@" ;; fix) shift; cmd_fix "$@" ;; scan) cmd_scan ;; nonreg) shift; cmd_nonreg "$@" ;; brain) shift; cmd_brain "$@" ;; deploy) shift; cmd_deploy "$@" ;; rate) cmd_rate ;; help|*) cmd_help ;; esac