#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # shellcheck source=/dev/null source "${ROOT_DIR}/src/app_v2.sh" usage() { cat </dev/null 2>&1; then echo "ai_vertical_slice.sh: jq is required" >&2 exit 2 fi } skip_evals="${AI_SLICE_SKIP_EVALS:-0}" auto_start_daemon="${AI_SLICE_AUTO_START_DAEMON:-0}" while [[ $# -gt 0 ]]; do case "$1" in --skip-evals) skip_evals=1 shift ;; --auto-start-daemon) auto_start_daemon=1 shift ;; -h|--help) usage exit 0 ;; *) usage >&2 exit 2 ;; esac done fixture_path="${AI_SLICE_FIXTURE_PATH:-${ROOT_DIR}/ai/fixtures/seed_batch.json}" roots_csv="${AI_SLICE_ROOTS_CSV:-doc-ai-1}" goals_csv="${AI_SLICE_GOALS_CSV:-ms.within_domain}" question="${AI_SLICE_QUESTION:-What domain is doc-ai-1 in?}" [[ -f "${fixture_path}" ]] || { echo "ai_vertical_slice.sh: fixture not found: ${fixture_path}" >&2 exit 2 } require_jq app_init ensure_daemon_ready() { if app_startup_checks >/dev/null 2>&1; then return 0 fi if [[ "${auto_start_daemon}" == "1" ]]; then local daemon_backend="${AI_DAEMON_STORE_BACKEND:-fs}" local daemon_root="${AI_DAEMON_STORE_ROOT:-/tmp/amduat-asl-ai-slice}" local daemon_log="${AI_DAEMON_LOG_PATH:-/tmp/ai-vertical-slice-daemon.log}" echo "daemon not reachable; attempting startup via scripts/dev_start_daemon.sh" >&2 STORE_BACKEND="${daemon_backend}" STORE_ROOT="${daemon_root}" SOCK="${SOCK}" SPACE="${SPACE}" \ nohup "${ROOT_DIR}/scripts/dev_start_daemon.sh" >"${daemon_log}" 2>&1 & local daemon_boot_pid="$!" disown "${daemon_boot_pid}" 2>/dev/null || true local i for i in $(seq 1 80); do if app_startup_checks >/dev/null 2>&1; then return 0 fi sleep 0.1 done app_startup_checks >/dev/null 2>&1 || { echo "ai_vertical_slice.sh: daemon still unreachable after startup attempt" >&2 echo "see ${daemon_log} for startup logs" >&2 return 1 } return 0 fi echo "ai_vertical_slice.sh: daemon unreachable on SOCK=${SOCK}" >&2 echo "hint: run ./scripts/dev_start_daemon.sh or pass --auto-start-daemon" >&2 return 1 } ensure_daemon_ready echo "== startup-check ==" app_startup_checks | jq . echo "== ingest fixture ==" idempotency_key="ai-slice-$(date +%s)" payload="$(jq -c --arg k "${idempotency_key}" '.idempotency_key = $k' "${fixture_path}")" ingest_out="$(app_ingest_batch "${payload}")" printf '%s\n' "${ingest_out}" | jq . echo "== retrieve context ==" retrieve_out="$(app_retrieve_with_fallback "${roots_csv}" "${goals_csv}")" printf '%s\n' "${retrieve_out}" | jq . printf '%s' "${retrieve_out}" | jq -e '((.nodes // []) | length) > 0 and ((.edges // []) | length) > 0' >/dev/null || { echo "ai_vertical_slice.sh: retrieve produced no graph context" >&2 exit 1 } echo "== grounded answer ==" answer_out="$(app_ai_answer_json "${roots_csv}" "${question}" "${goals_csv}" "1")" printf '%s\n' "${answer_out}" | jq . printf '%s' "${answer_out}" | jq -e '.grounding.has_evidence == true and (.response | type == "string" and length > 0)' >/dev/null || { echo "ai_vertical_slice.sh: answer was not grounded with evidence" >&2 exit 1 } if [[ "${skip_evals}" != "1" ]]; then echo "== evals ==" "${ROOT_DIR}/tests/ai_eval.sh" "${ROOT_DIR}/tests/ai_answer_eval.sh" else echo "== evals skipped ==" fi echo "ai_vertical_slice.sh: PASS"