Repository navigation
Expand file tree
/
Copy pathtest_compaction.sh
More file actions
executable file
·413 lines (344 loc) · 14.3 KB
/
Copy pathtest_compaction.sh
File metadata and controls
executable file
·413 lines (344 loc) · 14.3 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
#!/bin/bash
# Compaction smoke test script
# Tests both manual (trigger prompt) and auto compaction (threshold-based)
#
# Environment variable overrides:
# COMPACTION_PROVIDER - Override the provider for tests 1 & 2 (default: use system default)
# COMPACTION_MODEL - Override the model for tests 1 & 2 (default: use system default)
# SKIP_BUILD - Skip cargo build if set
if [ -f .env ]; then
export $(grep -v '^#' .env | xargs)
fi
if [ -z "$SKIP_BUILD" ]; then
echo "Building goose..."
cargo build --bin goose
echo ""
else
echo "Skipping build (SKIP_BUILD is set)..."
echo ""
fi
SCRIPT_DIR=$(pwd)
GOOSE_BIN="$SCRIPT_DIR/target/debug/goose"
# Apply provider/model overrides if set
if [ -n "$COMPACTION_PROVIDER" ]; then
echo "Using override provider: $COMPACTION_PROVIDER"
export GOOSE_PROVIDER="$COMPACTION_PROVIDER"
fi
if [ -n "$COMPACTION_MODEL" ]; then
echo "Using override model: $COMPACTION_MODEL"
export GOOSE_MODEL="$COMPACTION_MODEL"
fi
if [ -n "$COMPACTION_PROVIDER" ] || [ -n "$COMPACTION_MODEL" ]; then
echo ""
fi
# Print the error proxy's request ledger. Without this, a TEST 3 failure gives
# no way to tell which upstream requests were made or which one received the
# injected error, leaving the request ordering to guesswork.
dump_proxy_log() {
if [ -n "$PROXY_LOG" ] && [ -s "$PROXY_LOG" ]; then
echo " Proxy request log:"
cat "$PROXY_LOG"
fi
}
# Validation function to check compaction structure in session JSON
validate_compaction() {
local session_id=$1
local test_name=$2
echo "Validating compaction structure for session: $session_id"
# Export the session to JSON
local session_json=$($GOOSE_BIN session export --format json --session-id "$session_id" 2>&1)
if [ $? -ne 0 ]; then
echo "✗ FAILED: Could not export session JSON"
echo " Error: $session_json"
return 1
fi
if ! command -v jq &> /dev/null; then
echo "⚠ WARNING: jq not available, cannot validate compaction structure"
return 0
fi
# Check basic structure
echo "$session_json" | jq -e '.conversation' > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "✗ FAILED: Session JSON missing 'conversation' field"
return 1
fi
local message_count=$(echo "$session_json" | jq '.conversation | length' 2>/dev/null)
echo " Session has $message_count messages"
# Look for a summary message (assistant role with userVisible=false, agentVisible=true)
local has_summary=$(echo "$session_json" | jq '[.conversation[] | select(.role == "assistant" and .metadata.userVisible == false and .metadata.agentVisible == true)] | length > 0' 2>/dev/null)
if [ "$has_summary" != "true" ]; then
echo "✗ FAILED: No summary message found (expected assistant message with userVisible=false, agentVisible=true)"
return 1
fi
echo "✓ Found summary message with correct visibility flags"
# Check for original messages with userVisible=true, agentVisible=false
local has_hidden_originals=$(echo "$session_json" | jq '[.conversation[] | select(.metadata.userVisible == true and .metadata.agentVisible == false)] | length > 0' 2>/dev/null)
if [ "$has_hidden_originals" != "true" ]; then
echo "⚠ WARNING: No original messages found with userVisible=true, agentVisible=false"
echo " This might be OK if all messages were compacted"
else
echo "✓ Found original messages hidden from agent (userVisible=true, agentVisible=false)"
fi
# For auto-compaction, check for the preserved user message (userVisible=true, agentVisible=true)
local has_preserved_user=$(echo "$session_json" | jq '[.conversation[] | select(.role == "user" and .metadata.userVisible == true and .metadata.agentVisible == true)] | length > 0' 2>/dev/null)
if [ "$has_preserved_user" == "true" ]; then
echo "✓ Found preserved user message (userVisible=true, agentVisible=true)"
fi
echo "✓ SUCCESS: Compaction structure is valid for $test_name"
return 0
}
echo "=================================================="
echo "COMPACTION SMOKE TESTS"
echo "=================================================="
echo ""
RESULTS=()
# ==================================================
# TEST 1: Manual Compaction
# ==================================================
echo "---------------------------------------------------"
echo "TEST 1: Manual Compaction via trigger prompt"
echo "---------------------------------------------------"
TESTDIR=$(mktemp -d)
echo "hello world" > "$TESTDIR/hello.txt"
echo "Test directory: $TESTDIR"
echo ""
OUTPUT=$(mktemp)
echo "Step 1: Creating session with initial messages..."
(cd "$TESTDIR" && "$GOOSE_BIN" run --with-builtin developer --text "list files and read hello.txt" 2>&1) | tee "$OUTPUT"
if ! command -v jq &> /dev/null; then
echo "✗ FAILED: jq is required for this test"
RESULTS+=("✗ Manual Compaction (jq required)")
rm -f "$OUTPUT"
rm -rf "$TESTDIR"
else
SESSION_ID=$("$GOOSE_BIN" session list --format json 2>/dev/null | jq -r '.[0].id' 2>/dev/null)
if [ -z "$SESSION_ID" ] || [ "$SESSION_ID" = "null" ]; then
echo "✗ FAILED: Could not create session"
RESULTS+=("✗ Manual Compaction (no session)")
else
echo ""
echo "Session created: $SESSION_ID"
echo "Step 2: Sending manual compaction trigger..."
# Send the manual compact trigger prompt
(cd "$TESTDIR" && "$GOOSE_BIN" run --resume --session-id "$SESSION_ID" --text "Please compact this conversation" 2>&1) | tee -a "$OUTPUT"
echo ""
echo "Checking for compaction evidence..."
if grep -qi "compacting\|compacted\|compaction" "$OUTPUT"; then
echo "✓ SUCCESS: Manual compaction was triggered"
if validate_compaction "$SESSION_ID" "manual compaction"; then
RESULTS+=("✓ Manual Compaction")
else
RESULTS+=("✗ Manual Compaction (structure validation failed)")
fi
else
echo "✗ FAILED: Manual compaction was not triggered"
RESULTS+=("✗ Manual Compaction")
fi
fi
rm -f "$OUTPUT"
rm -rf "$TESTDIR"
fi
echo ""
echo ""
# ==================================================
# TEST 2: Auto Compaction
# ==================================================
echo "---------------------------------------------------"
echo "TEST 2: Auto Compaction via threshold (0.005)"
echo "---------------------------------------------------"
TESTDIR=$(mktemp -d)
echo "test content" > "$TESTDIR/test.txt"
echo "Test directory: $TESTDIR"
echo ""
# Set auto-compact threshold very low (.5%) to trigger it quickly
export GOOSE_AUTO_COMPACT_THRESHOLD=0.005
OUTPUT=$(mktemp)
LONG_RESPONSE_PROMPT="Count from 1 to 200, one number per line."
echo "Step 1: Creating session with first message (generating tokens for threshold)..."
(cd "$TESTDIR" && "$GOOSE_BIN" run --text "$LONG_RESPONSE_PROMPT" 2>&1) | tee "$OUTPUT"
if ! command -v jq &> /dev/null; then
echo "✗ FAILED: jq is required for this test"
RESULTS+=("✗ Auto Compaction (jq required)")
else
SESSION_ID=$("$GOOSE_BIN" session list --format json 2>/dev/null | jq -r '.[0].id' 2>/dev/null)
if [ -z "$SESSION_ID" ] || [ "$SESSION_ID" = "null" ]; then
echo "✗ FAILED: Could not create session"
RESULTS+=("✗ Auto Compaction (no session)")
else
echo ""
echo "Session created: $SESSION_ID"
echo "Step 2: Sending second message (should trigger auto-compact)..."
# Send second message - auto-compaction should trigger before processing this
(cd "$TESTDIR" && "$GOOSE_BIN" run --resume --session-id "$SESSION_ID" --text "hi again" 2>&1) | tee -a "$OUTPUT"
echo ""
echo "Checking for auto-compaction evidence..."
if grep -qi "auto.*compact\|exceeded.*auto.*compact.*threshold" "$OUTPUT"; then
echo "✓ SUCCESS: Auto compaction was triggered"
if validate_compaction "$SESSION_ID" "auto compaction"; then
RESULTS+=("✓ Auto Compaction")
else
RESULTS+=("✗ Auto Compaction (structure validation failed)")
fi
else
echo "✗ FAILED: Auto compaction was not triggered"
echo " Expected to see auto-compact messages with threshold of 0.005"
RESULTS+=("✗ Auto Compaction")
fi
fi
fi
# Unset the env variable
unset GOOSE_AUTO_COMPACT_THRESHOLD
rm -f "$OUTPUT"
rm -rf "$TESTDIR"
echo ""
echo ""
# ==================================================
# TEST 3: Out-of-Context Error Compaction
# ==================================================
echo "---------------------------------------------------"
echo "TEST 3: Compaction via out-of-context error (proxy)"
echo "---------------------------------------------------"
TESTDIR=$(mktemp -d)
echo "test content" > "$TESTDIR/test.txt"
echo "Test directory: $TESTDIR"
echo ""
# Use a random port to avoid conflicts
PROXY_PORT=$((9000 + RANDOM % 1000))
PROXY_DIR="$SCRIPT_DIR/scripts/provider-error-proxy"
OUTPUT=$(mktemp)
PROXY_LOG=$(mktemp)
PROXY_SETUP_LOG=$(mktemp)
# Pre-install proxy dependencies (so first run doesn't take forever)
echo "Installing proxy dependencies..."
export UV_INDEX_URL="https://pypi.org/simple"
if ! (cd "$PROXY_DIR" && uv sync 2>&1 | tee "$PROXY_SETUP_LOG"); then
echo "✗ FAILED: Could not install proxy dependencies"
echo "Setup log:"
cat "$PROXY_SETUP_LOG"
RESULTS+=("✗ Out-of-Context Error (dependency install failed)")
else
echo "✓ Dependencies installed"
# Start the error proxy in context-length error mode.
#
# Inject exactly ONE error. The proxy only injects into completion requests
# (see COMPLETION_PATHS in provider-error-proxy/proxy.py), so this error lands
# on the turn completion and the summarizer call that follows succeeds,
# producing the summary message this test validates.
#
# Do not raise this number to "give compaction more retries". A context-length
# error is deterministic for a fixed prompt, so retrying an identical request
# is not a scenario worth asserting; a higher count just consumes the
# summarizer's own calls and prevents the summary this test exists to check.
echo "Starting error proxy on port $PROXY_PORT with context-length error mode..."
(cd "$PROXY_DIR" && UV_INDEX_URL="https://pypi.org/simple" uv run proxy.py --port "$PROXY_PORT" --mode "c 1" --no-stdin > "$PROXY_LOG" 2>&1) &
PROXY_PID=$!
# Wait for proxy to be ready (check if port is listening)
echo "Waiting for proxy to be ready..."
PROXY_READY=false
for i in {1..60}; do
if kill -0 $PROXY_PID 2>/dev/null; then
# Check if port is listening using /dev/tcp
if timeout 1 bash -c "echo -n > /dev/tcp/localhost/$PROXY_PORT" 2>/dev/null; then
PROXY_READY=true
echo "✓ Proxy is ready on port $PROXY_PORT"
break
fi
else
echo "✗ FAILED: Error proxy process died"
break
fi
sleep 0.5
done
# Check if proxy is running and ready
if [ "$PROXY_READY" != "true" ]; then
echo "✗ FAILED: Error proxy failed to become ready"
echo "Proxy log:"
cat "$PROXY_LOG"
kill $PROXY_PID 2>/dev/null || true
RESULTS+=("✗ Out-of-Context Test Error (proxy failed)")
else
# Configure provider to use proxy and skip backoff
export ANTHROPIC_HOST="http://localhost:$PROXY_PORT"
export GOOSE_PROVIDER_SKIP_BACKOFF=true
export GOOSE_PROVIDER=anthropic
export GOOSE_MODEL=claude-haiku-4-5
# Session naming runs as a background completion request spawned at the
# start of the turn, so it races the turn completion for the proxy's single
# injected error. When it wins, the naming call absorbs the error (it only
# logs a warning), the turn succeeds, and no compaction ever happens.
# Disable it so the turn completion is deterministically the first
# completion request the proxy sees.
export GOOSE_DISABLE_SESSION_NAMING=true
echo "Step 1: Creating session (should trigger context-length error and compaction)..."
(cd "$TESTDIR" && "$GOOSE_BIN" run --text "hello world" 2>&1) | tee "$OUTPUT"
SESSION_ID=$("$GOOSE_BIN" session list --format json 2>/dev/null | jq -r '.[0].id' 2>/dev/null)
if [ -z "$SESSION_ID" ] || [ "$SESSION_ID" = "null" ]; then
echo "✗ FAILED: Could not create session"
RESULTS+=("✗ Out-of-Context Test Error (no session)")
else
echo ""
echo "Session created: $SESSION_ID"
echo "Checking for compaction evidence..."
# The compaction failure message itself contains the word "compact", so a
# bare keyword grep matches it and reports success on a failed run. Fail
# explicitly on the error text before checking for evidence of compaction.
if grep -qi "error trying to compact" "$OUTPUT"; then
echo "✗ FAILED: Compaction was attempted but returned an error"
echo " Output:"
cat "$OUTPUT"
dump_proxy_log
RESULTS+=("✗ Out-of-Context Test Error (compaction errored)")
elif grep -qi "context.*length\|compacting\|compacted\|compaction" "$OUTPUT"; then
echo "✓ SUCCESS: Out-of-context Test error triggered compaction"
if validate_compaction "$SESSION_ID" "out-of-context error compaction"; then
RESULTS+=("✓ Out-of-Context Test Error")
else
dump_proxy_log
RESULTS+=("✗ Out-of-Context Test Error (structure validation failed)")
fi
else
echo "✗ FAILED: No evidence of compaction after context-length error"
echo " Output:"
cat "$OUTPUT"
dump_proxy_log
RESULTS+=("✗ Out-of-Context Test Error")
fi
fi
# Clean up
echo ""
echo "Stopping error proxy..."
# Kill the entire process group to ensure UV and Python processes are terminated
kill -- -$PROXY_PID 2>/dev/null || true
# Also explicitly kill any remaining UV processes on this port
pkill -f "uv run.*--port $PROXY_PORT" 2>/dev/null || true
wait $PROXY_PID 2>/dev/null || true
unset ANTHROPIC_HOST
unset GOOSE_PROVIDER_SKIP_BACKOFF
unset GOOSE_PROVIDER
unset GOOSE_MODEL
unset UV_INDEX_URL
fi
fi
rm -f "$OUTPUT" "$PROXY_LOG" "$PROXY_SETUP_LOG"
rm -rf "$TESTDIR"
echo ""
echo ""
# ==================================================
# Summary
# ==================================================
echo "=================================================="
echo "TEST SUMMARY"
echo "=================================================="
for result in "${RESULTS[@]}"; do
echo "$result"
done
# Count results
FAILURE_COUNT=$(echo "${RESULTS[@]}" | grep -o "✗" | wc -l | tr -d ' ')
if [ "$FAILURE_COUNT" -gt 0 ]; then
echo ""
echo "❌ $FAILURE_COUNT test(s) failed!"
exit 1
else
echo ""
echo "✅ All tests passed!"
fi