lib.sh 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. # Shared smoketest helpers, sourced by smoketest/run.sh. Kept byte-identical
  2. # across the spark-* container repos.
  3. #
  4. # A case is a shell function named t_<something>; run_all runs them in file
  5. # order. Checks are jq expressions that must be true, so a new kind of check
  6. # never needs a change here -- this file only moves requests and counts results.
  7. #
  8. # smoketest/run.sh <base-url> [served-name]
  9. # ONLY=t_thinking_on smoketest/run.sh <base-url> # one case
  10. #
  11. # Exit status is the number of failed cases.
  12. set -uo pipefail
  13. BASE="${1:?usage: run.sh <base-url> [served-name]}"
  14. BASE="${BASE%/}"
  15. HERE="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)"
  16. ROOT="$(dirname "$HERE")"
  17. NAME="${2:-$(sed -n 's/^name: *//p' "$ROOT/model.yaml" | head -1)}"
  18. TIMEOUT="${TIMEOUT:-300}"
  19. PASS=0 FAIL=0 CASE="" STATUS="" BODY="" SECS=""
  20. TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
  21. for tool in curl jq; do
  22. command -v "$tool" >/dev/null || { echo "smoketest needs $tool"; exit 2; }
  23. done
  24. # post PATH JSON -> STATUS, BODY, SECS. The body goes through stdin: a single
  25. # argv string is capped at 128 KiB on Linux, and a data URL passes that fast.
  26. post() {
  27. local out
  28. out=$(printf '%s' "$2" | curl -s --max-time "$TIMEOUT" -w '\n%{http_code} %{time_total}' \
  29. -H 'Content-Type: application/json' --data-binary @- "$BASE$1")
  30. BODY="${out%$'\n'*}"; read -r STATUS SECS <<<"${out##*$'\n'}"
  31. }
  32. # post_form PATH curl-form-args... -> STATUS, BODY, SECS (multipart uploads)
  33. post_form() {
  34. local path="$1" out; shift
  35. out=$(curl -s --max-time "$TIMEOUT" -w '\n%{http_code} %{time_total}' "$@" "$BASE$path")
  36. BODY="${out%$'\n'*}"; read -r STATUS SECS <<<"${out##*$'\n'}"
  37. }
  38. # jqb EXPR: evaluate EXPR against BODY, via a file rather than argv.
  39. jqb() { printf '%s' "$BODY" > "$TMP/body.json"; jq -r "$@" "$TMP/body.json"; }
  40. # check DESCRIPTION JQ-BOOLEAN: fail the case unless EXPR is true of BODY.
  41. check() {
  42. printf '%s' "$BODY" > "$TMP/body.json"
  43. if ! jq -e "$2" "$TMP/body.json" >/dev/null 2>&1; then
  44. FAIL=$((FAIL + 1))
  45. echo " FAIL ${CASE#t_}: $1"
  46. echo " status $STATUS, body: ${BODY:0:400}"
  47. return 1
  48. fi
  49. }
  50. # count_tokens TEXT -> the model's own token count, via /tokenize.
  51. count_tokens() {
  52. jq -n --arg m "$NAME" --arg p "$1" '{model:$m, prompt:$p, add_special_tokens:false}' \
  53. | curl -s --max-time 60 -H 'Content-Type: application/json' --data-binary @- "$BASE/tokenize" \
  54. | jq -r '.count'
  55. }
  56. ok() {
  57. local s="$SECS"; [ "$s" != - ] && s=$(printf '%.2f' "$s")
  58. PASS=$((PASS + 1)); echo " ok ${CASE#t_} (${s}s${1:+, $1})"
  59. }
  60. run_all() {
  61. echo "smoketest $NAME against $BASE"
  62. local c
  63. for c in $(grep -oE '^t_[a-z0-9_]+\(\)' "${BASH_SOURCE[1]}" | tr -d '()'); do
  64. [ -n "${ONLY:-}" ] && [ "$c" != "$ONLY" ] && continue
  65. CASE="$c"; STATUS=""; BODY=""; SECS="-"
  66. "$c" || true
  67. done
  68. echo "$PASS/$((PASS + FAIL)) passed"
  69. exit "$FAIL"
  70. }