303 lines
13 KiB
Bash
Executable File
303 lines
13 KiB
Bash
Executable File
#!/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 <question>"; 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 <file|dir>"; 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 <file> [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 <file|dir> [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 <question> — 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} <q> AI Q&A (auto-failover chain)"
|
|
echo -e " ${G}droid local${W} <q> Force Ollama local (unlimited)"
|
|
echo -e " ${G}droid analyze${W} <path> AI code analysis"
|
|
echo -e " ${G}droid fix${W} <file> 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 <dom>]"
|
|
echo -e " ${G}droid deploy${W} <s> [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
|