#!/bin/bash
# Launch PD-separated pair (TP=1 each) for lifecycle microbenchmark.
# Uses GPUs 1 (prefill) and 2 (decode) to avoid conflicting with Microbench 1 on GPU 0.
#
# Usage: bash launch_pd_pair.sh
#   Requires: ~/agentic-kv/.venv with vLLM 0.18.1 + Mooncake

set -euo pipefail

VENV="$HOME/agentic-kv/.venv/bin"
PYTHON="$VENV/python"
MODEL_PATH="${MODEL_PATH:-$HOME/models/Qwen/Qwen3-Coder-30B-A3B-Instruct}"

PREFILL_PORT=8010
DECODE_PORT=8020
BOOTSTRAP_PORT=8998

PREFILL_GPU=1
DECODE_GPU=2

LOG_DIR="$HOME/agentic-kv/microbench/lifecycle/logs"
mkdir -p "$LOG_DIR"

trap 'echo "Cleaning up..."; kill $(jobs -p) 2>/dev/null; wait 2>/dev/null' EXIT INT TERM

echo "=== PD Lifecycle Microbench: PD-separated pair ==="
echo "  Model:     $MODEL_PATH"
echo "  Prefill:   GPU $PREFILL_GPU, port $PREFILL_PORT, bootstrap $BOOTSTRAP_PORT"
echo "  Decode:    GPU $DECODE_GPU, port $DECODE_PORT"
echo ""

# Start prefill instance (KV producer)
echo "[1/2] Starting prefill instance on GPU $PREFILL_GPU..."
VLLM_MOONCAKE_BOOTSTRAP_PORT=$BOOTSTRAP_PORT \
VLLM_PD_PROFILE_LOG="$LOG_DIR/prefill_events.jsonl" \
CUDA_VISIBLE_DEVICES=$PREFILL_GPU \
$PYTHON -m vllm.entrypoints.openai.api_server \
    --model "$MODEL_PATH" \
    --host 0.0.0.0 \
    --port $PREFILL_PORT \
    --tensor-parallel-size 1 \
    --trust-remote-code \
    --enable-prefix-caching \
    --dtype auto \
    --gpu-memory-utilization 0.9 \
    --max-model-len 200000 \
    --no-enable-log-requests \
    --kv-transfer-config \
    '{"kv_connector":"MooncakeConnector","kv_role":"kv_producer"}' \
    2>&1 | tee "$LOG_DIR/prefill.log" &
PREFILL_PID=$!
echo "  Prefill PID=$PREFILL_PID"

# Wait for prefill to be ready
echo "  Waiting for prefill instance..."
for i in $(seq 1 180); do
    if curl -s "http://127.0.0.1:$PREFILL_PORT/v1/models" > /dev/null 2>&1; then
        echo "  Prefill ready after ${i}s"
        break
    fi
    if [ $i -eq 180 ]; then
        echo "  ERROR: Prefill did not start within 180s"
        exit 1
    fi
    sleep 1
done

# Start decode instance (KV consumer)
echo "[2/2] Starting decode instance on GPU $DECODE_GPU..."
VLLM_PD_PROFILE_LOG="$LOG_DIR/decode_events.jsonl" \
CUDA_VISIBLE_DEVICES=$DECODE_GPU \
$PYTHON -m vllm.entrypoints.openai.api_server \
    --model "$MODEL_PATH" \
    --host 0.0.0.0 \
    --port $DECODE_PORT \
    --tensor-parallel-size 1 \
    --trust-remote-code \
    --enable-prefix-caching \
    --dtype auto \
    --gpu-memory-utilization 0.9 \
    --max-model-len 200000 \
    --no-enable-log-requests \
    --kv-transfer-config \
    "{\"kv_connector\":\"MooncakeConnector\",\"kv_role\":\"kv_consumer\",\"kv_connector_extra_config\":{\"prefill_addr\":\"127.0.0.1:$BOOTSTRAP_PORT\"}}" \
    2>&1 | tee "$LOG_DIR/decode.log" &
DECODE_PID=$!
echo "  Decode PID=$DECODE_PID"

# Wait for decode to be ready
echo "  Waiting for decode instance..."
for i in $(seq 1 180); do
    if curl -s "http://127.0.0.1:$DECODE_PORT/v1/models" > /dev/null 2>&1; then
        echo "  Decode ready after ${i}s"
        break
    fi
    if [ $i -eq 180 ]; then
        echo "  ERROR: Decode did not start within 180s"
        exit 1
    fi
    sleep 1
done

echo ""
echo "=== Both instances ready ==="
echo "  Prefill: http://127.0.0.1:$PREFILL_PORT (PID $PREFILL_PID)"
echo "  Decode:  http://127.0.0.1:$DECODE_PORT (PID $DECODE_PID)"
echo ""
echo "  Prefill PID: $PREFILL_PID" > "$LOG_DIR/.pids"
echo "  Decode PID: $DECODE_PID" >> "$LOG_DIR/.pids"
echo "$PREFILL_PID" > "$LOG_DIR/.prefill.pid"
echo "$DECODE_PID" > "$LOG_DIR/.decode.pid"

echo "Press Ctrl+C to stop both instances."
wait
