summaryrefslogtreecommitdiffstats
path: root/test_prompts.sh
blob: 61fb69fb8c9ff65c6e577d56a01fa19758befc41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/sh

# Usage: ./test_prompts.sh <model_path> [prompt_file]

SOCK_PATH="/var/run/lex.sock"
EXECUTABLE="./lex"

# Mandatory Model Argument
MODEL_PATH="$1"

if [ -z "$MODEL_PATH" ]; then
    echo "Usage: $0 <model_path> [prompt_file]" >&2
    exit 1
fi

if [ ! -f "$MODEL_PATH" ]; then
    echo "Error: Model file '$MODEL_PATH' not found." >&2
    exit 1
fi

# Derive base model name and output result filename
# e.g., models/qwen2.5-7b-q4.gguf -> results_qwen2.5-7b-q4.gguf.txt
MODEL_NAME=$(basename "$MODEL_PATH")
RESULT_FILE="results_${MODEL_NAME}.txt"

# Default prompt filename (optional 2nd argument)
PROMPT_FILE="${2:-test_prompts.txt}"

# Ensure the executable exists and is runnable
if [ ! -x "$EXECUTABLE" ]; then
    echo "Error: Executable '$EXECUTABLE' not found or not executable." >&2
    exit 1
fi

# Ensure the prompt file exists
if [ ! -f "$PROMPT_FILE" ]; then
    echo "Error: Prompt file '$PROMPT_FILE' not found." >&2
    exit 1
fi

# Count valid prompts using POSIX awk to avoid ksh $(...) parenthetical syntax errors
total_prompts=$(awk '/^[[:space:]]*($|#)/ {next} {count++} END {print count+0}' "$PROMPT_FILE")

if [ "$total_prompts" -eq 0 ]; then
    echo "No valid prompts found in '$PROMPT_FILE'."
    exit 0
fi

# Create (or truncate) the result file at the start of the run
> "$RESULT_FILE"

# Create horizontal rule.
RULE=""
i=0
while [ $i -lt 72 ]; do
    RULE="${RULE}-"
    i=$((i + 1))
done

# --- Start the daemon once, wait for it to come up, capture its baseline RSS ---

echo "Starting daemon with model: '$MODEL_PATH'"
"$EXECUTABLE" -m "$MODEL_PATH" &
DAEMON_PID=$!

cleanup() {
    kill "$DAEMON_PID" 2>/dev/null
    wait "$DAEMON_PID" 2>/dev/null
    rm -f "$TIME_LOG"
}
trap cleanup EXIT INT TERM

# Wait for the socket to appear (covers model load time) and for the
# process to still be alive (covers early startup failure, e.g. bad
# model path -> daemon exits immediately).
waited=0
while [ ! -S "$SOCK_PATH" ]; do
    if ! kill -0 "$DAEMON_PID" 2>/dev/null; then
        echo "Error: daemon exited during startup (bad model path?)" >&2
        exit 1
    fi
    sleep 1
    waited=$((waited + 1))
    if [ "$waited" -ge 120 ]; then
        echo "Error: timed out waiting for daemon socket at $SOCK_PATH" >&2
        exit 1
    fi
done

echo "Daemon ready (pid $DAEMON_PID) after ${waited}s"
echo "Evaluating $total_prompts prompt(s) from '$PROMPT_FILE'"
echo "Writing results to: '$RESULT_FILE'"
echo "---------------------------------------------------"

current=0
first=1

# Temporary file to capture time / rusage output
TIME_LOG=$(mktemp /tmp/lex_time.XXXXXX)

# Process each prompt line by line
while IFS= read -r prompt || [ -n "$prompt" ]; do
    # Skip empty lines or comment lines starting with #
    case "$prompt" in
        ""|\#*) continue ;;
    esac

    current=$((current + 1))

    # Add delimiter before every prompt except the first in the results file
    if [ "$first" -eq 1 ]; then
        first=0
    else
        printf "%s\n\n" "$RULE" >> "$RESULT_FILE"
    fi

    # Log prompt header to the result file
    printf "PROMPT: %s\n\n" "$prompt" >> "$RESULT_FILE"

    # Sample the daemon's RSS right before firing the request, so we have
    # a rough steady-state figure (won't catch a mid-request peak, but the
    # model's footprint is dominated by the loaded weights, not per-request
    # allocation, so this is a reasonable proxy).
    daemon_rss_kb=$(ps -o rss= -p "$DAEMON_PID" 2>/dev/null | tr -d ' ')

    # Send the prompt to the daemon over the socket, timing the round trip.
    # This now measures request latency only (no model-load cost), which is
    # the number that actually matters day to day with the daemon design.
    /usr/bin/time -lp sh -c "printf '%s\n' \"\$1\" | nc -U \"\$2\"" _ "$prompt" "$SOCK_PATH" \
        >> "$RESULT_FILE" 2> "$TIME_LOG"

    real_time=$(awk '/^real/ {print $2}' "$TIME_LOG")

    if [ -n "$daemon_rss_kb" ]; then
        daemon_rss_mb=$(awk -v kb="$daemon_rss_kb" 'BEGIN{printf "%.2f", kb/1024}')
        metrics_summary="ELAPSED: ${real_time}s | DAEMON RSS: ${daemon_rss_mb} MB"
    else
        metrics_summary="ELAPSED: ${real_time}s | DAEMON RSS: unavailable"
    fi

    printf "METRICS: %s\n\n" "$metrics_summary" >> "$RESULT_FILE"

    printf "[%d/%d] Done: %s (%s)\n" "$current" "$total_prompts" "$prompt" "$metrics_summary"

done < "$PROMPT_FILE"

echo "---------------------------------------------------"
echo "Done! Processed $total_prompts prompt(s)."