#!/usr/bin/env bash set -euo pipefail GATEWAY_URL="${GATEWAY_URL:-http://localhost:9300}" ROUTER_URL="${ROUTER_URL:-http://localhost:9102}" MEMORY_URL="${MEMORY_URL:-http://localhost:8000}" SWAPPER_URL="${SWAPPER_URL:-http://localhost:8890}" SMOKE_USER="${SMOKE_USER:-smoke-helion-user}" SMOKE_CHAT="${SMOKE_CHAT:-smoke-helion-chat}" note() { echo "[INFO] $*"; } ok() { echo "[OK] $*"; } fail() { echo "[FAIL] $*" >&2; exit 1; } need_cmd() { command -v "$1" >/dev/null 2>&1 || fail "missing command: $1" } need_cmd curl need_cmd python3 note "Health checks" curl -fsS -m 10 "$GATEWAY_URL/health" >/dev/null || fail "gateway health failed" curl -fsS -m 10 "$ROUTER_URL/health" >/dev/null || fail "router health failed" curl -fsS -m 10 "$MEMORY_URL/health" >/dev/null || fail "memory health failed" curl -fsS -m 10 "$SWAPPER_URL/health" >/dev/null || fail "swapper health failed" ok "core services healthy" note "Helion text infer (deepseek-first path)" TEXT_RESP="$(curl -fsS -m 45 -H 'Content-Type: application/json' \ -d "{\"prompt\":\"Що таке тепловий насос? Коротко.\",\"agent\":\"helion\",\"metadata\":{\"source\":\"ops-smoke\",\"user_id\":\"$SMOKE_USER\",\"chat_id\":\"$SMOKE_CHAT\"}}" \ "$ROUTER_URL/v1/agents/helion/infer")" python3 - "$TEXT_RESP" <<'PY' || fail "text infer validation failed" import json, sys data = json.loads(sys.argv[1]) backend = str(data.get("backend", "")) resp = str(data.get("response", "")) if not backend: raise SystemExit("backend missing") if backend == "crewai": raise SystemExit("unexpected crewai backend for short text") if not resp: raise SystemExit("empty response") print(f"[INFO] text backend={backend}") PY ok "text infer passed" note "Memory write/read smoke" curl -fsS -m 15 -H "Authorization: Bearer $SMOKE_USER" -H 'Content-Type: application/json' \ -d "{\"agent_id\":\"helion\",\"team_id\":\"helion-dao\",\"channel_id\":\"$SMOKE_CHAT\",\"user_id\":\"$SMOKE_USER\",\"scope\":\"short_term\",\"kind\":\"message\",\"body_text\":\"smoke memory user turn\",\"body_json\":{\"type\":\"user_message\",\"source\":\"ops-smoke\"}}" \ "$MEMORY_URL/agents/helion/memory" >/dev/null curl -fsS -m 15 -H "Authorization: Bearer $SMOKE_USER" -H 'Content-Type: application/json' \ -d "{\"agent_id\":\"helion\",\"team_id\":\"helion-dao\",\"channel_id\":\"$SMOKE_CHAT\",\"user_id\":\"$SMOKE_USER\",\"scope\":\"short_term\",\"kind\":\"message\",\"body_text\":\"smoke memory assistant turn\",\"body_json\":{\"type\":\"agent_response\",\"source\":\"ops-smoke\"}}" \ "$MEMORY_URL/agents/helion/memory" >/dev/null MEM_RESP="$(curl -fsS -m 15 -H "Authorization: Bearer $SMOKE_USER" \ "$MEMORY_URL/agents/helion/memory?user_id=$SMOKE_USER&channel_id=$SMOKE_CHAT&limit=10")" python3 - "$MEM_RESP" <<'PY' || fail "memory read validation failed" import json, sys data = json.loads(sys.argv[1]) events = data.get("events", []) joined = "\n".join(str(e.get("content", "")) for e in events) if "smoke memory user turn" not in joined or "smoke memory assistant turn" not in joined: raise SystemExit("smoke events not found in memory") print(f"[INFO] memory events={len(events)}") PY ok "memory smoke passed" note "Helion vision infer path" PNG_B64='iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO5X7h8AAAAASUVORK5CYII=' VISION_RESP="$(curl -fsS -m 90 -H 'Content-Type: application/json' \ -d "{\"prompt\":\"Що зображено? Відповідай коротко.\",\"agent\":\"helion\",\"images\":[\"data:image/png;base64,$PNG_B64\"],\"metadata\":{\"source\":\"ops-smoke\",\"user_id\":\"$SMOKE_USER\",\"chat_id\":\"$SMOKE_CHAT\"}}" \ "$ROUTER_URL/v1/agents/helion/infer")" python3 - "$VISION_RESP" <<'PY' || fail "vision infer validation failed" import json, sys data = json.loads(sys.argv[1]) backend = str(data.get("backend", "")) if not backend.startswith("swapper-vision"): raise SystemExit(f"unexpected vision backend: {backend}") print(f"[INFO] vision backend={backend}") PY ok "vision smoke passed" echo "[OK] helion stack smoke passed"