#!/usr/bin/env bash
# llm-keepwarm.sh — Ping inference servers to keep model weights hot in unified memory.
#
# Sends a minimal chat completion (max_tokens=1) to each active server. Without
# periodic pings, macOS memory pressure can evict inactive pages to swap, causing
# multi-second latency spikes on the next real request.
#
# Run manually or via cron. Example (every 4 minutes):
#   */4 * * * * /path/to/scripts/llm-keepwarm.sh

source "$(dirname "$0")/llm.conf"

LOG="$LOG_DIR/keepwarm.log"

_ts() { date '+%Y-%m-%d %H:%M:%S'; }
_log() { echo "$(_ts) $*" >> "$LOG"; }

ping_model() {
  local port="$1" label="$2"

  local model_id
  model_id=$(curl -sf --max-time 5 "http://localhost:${port}/v1/models" | \
    python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['id'])" 2>/dev/null)

  if [[ -z "$model_id" ]]; then
    _log "$label ✗ server not responding on :${port}"
    return 1
  fi

  local result
  result=$(curl -sf --max-time 30 "http://localhost:${port}/v1/chat/completions" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model_id\",\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}],\"max_tokens\":1,\"temperature\":0,\"stream\":false}" \
    -o /dev/null -w "%{http_code}" 2>/dev/null)

  if [[ "$result" == "200" ]]; then
    _log "$label ✓ warm on :${port} (HTTP 200)"
    return 0
  else
    _log "$label ✗ completion failed on :${port} (HTTP $result)"
    return 1
  fi
}

ping_model "$PRIMARY_PORT" "primary"
