Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions apps/ambient-inventory-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ or instance role).

### Run

```bash
./setup_demo.sh # reseeds, then runs the app on :8008
```

Equivalent by hand, in this order — the reseed has to land while the server is down:

```bash
python seed_demo.py --reset
uvicorn app.main:app --port 8008
Expand All @@ -166,21 +172,26 @@ the first chat message from stalling on stage. Check it worked:
curl -s localhost:8008/api/health | python -m json.tool
```

`mcp.ready` must be `true`. Then open `http://localhost:8008/?fresh=1` for a
clean run — it resets the scenario and starts a new delayed alert. The `fresh=1`
parameter is stripped from the URL immediately, so an accidental refresh
mid-demo will not wipe the scenario.
`mcp.ready` must be `true`. Then open `http://localhost:8008/` and press **Start
demo** when you begin.

Reseeding is a pre-flight step, not something the page does, which is why
`setup_demo.sh` does it before starting the server. Neither loading the page nor
pressing **Start demo** reseeds — `/api/demo/start` mints a new `session_id`, which
leaves the previous run's alert and transcript behind but does *not* clear
`purchase_orders`. That matters, because a leftover order for the shared component
makes the next sweep decide no alert is needed, so run `./setup_demo.sh` again
between runs. Refreshing mid-demo is safe.

Avoid `--reload` during a rehearsal: the pending alert lives in an in-process
task and a reload cancels it.

## Stage flow

Seed first, then open the app and walk away:
Start it, then open the app and walk away:

```bash
python seed_demo.py --reset
uvicorn app.main:app --port 8008 # wait for [mcp] connected
./setup_demo.sh # wait for [mcp] connected
open http://localhost:8008/
```

Expand Down
22 changes: 22 additions & 0 deletions apps/ambient-inventory-agent/setup_demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
#
# Reseed, then run the app on :8008. Ctrl+C stops it.
#
# ./setup_demo.sh
#
# Reseeding every run is required: approving the order writes a purchase_orders doc for
# the shared component, and the next sweep skips the alert because an order is already
# inbound. Pressing "Start demo" in the UI does not reseed.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"

PY=${PY:-.venv/bin/python}
[[ -x "$PY" ]] || PY=python3

[[ -n "${1:-}" ]] && { echo "ERROR: unknown option '$1' (takes none)" >&2; exit 1; }

echo "==> Reseeding the demo database"
"$PY" seed_demo.py --reset

echo "==> App: http://localhost:8008/ (wait for '[mcp] connected'; Ctrl+C to stop)"
exec "$PY" -m uvicorn app.main:app --port 8008
30 changes: 24 additions & 6 deletions apps/remote-mcp-perf-triage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ would normally require a developer who knows exactly where to look.

| File | Purpose |
|------|---------|
| `setup_demo.sh` | Resets the demo, then runs the checkout page. `--drop` for a full reseed. |
| `trickle.sh` | `start`/`stop`/`status` for the Performance Advisor trickle. |
| `checkout_app.py` | The Leafy Electronics checkout page. Really hangs on the slow poll, then fires the incident to ChatGPT. |
| `seed_payments.py` | Seeds a large, realistic `payments` collection (no index on `session_id`); `--drop-index` resets the demo. |
| `generate_load.py` | Runs the checkout status-poll query repeatedly to feed Performance Advisor. |
Expand Down Expand Up @@ -144,7 +146,7 @@ serves a Leafy Electronics checkout that runs the **real** status-poll query aga
unindexed collection — so the hang isn't staged, it's the bug:

```bash
python checkout_app.py # http://127.0.0.1:8000
./setup_demo.sh # resets, then serves http://127.0.0.1:8000
python checkout_app.py --no-incident # rehearse without paging the agent
```

Expand Down Expand Up @@ -245,27 +247,43 @@ completes sub-millisecond — well under any checkout timeout.

## Reset (to re-run the demo)

`setup_demo.sh` does the reset and then runs the checkout page, so one command per
demo run is enough:

```bash
./setup_demo.sh # light reset: drops the index, keeps the 300k docs
./setup_demo.sh --drop # full reseed: fresh 300k, several minutes
```

The trickle is managed separately, since it runs for hours across many demo runs:

```bash
./trickle.sh start # kills any existing trickle, then starts one fresh
./trickle.sh status
./trickle.sh stop # end of day
```

### Light reset (usual case)

If the collection is intact and you just created the index during the demo, drop the
index so the slow condition returns:
If the collection is intact and you just created the index during the demo, dropping
the index restores the slow condition — that is all `./setup_demo.sh` does. By hand:

```bash
python seed_payments.py --drop-index # drops the index, keeps the 300k docs
```

This exits before the insert loop, so your data is untouched. (Equivalent by hand:
This exits before the insert loop, so your data is untouched. (Equivalent in the shell:
`db.payments.dropIndex("session_id_1_status_1")`.)

Then make sure the trickle is running again ahead of the next run:
Keep the trickle running ahead of the next run — `./trickle.sh start`, or by hand:

```bash
nohup python generate_load.py > load.log 2>&1 & # background; tail -f load.log to watch
```

### Full reseed ritual (fresh data)

If you need to rebuild the collection from scratch:
To rebuild the collection from scratch, `./setup_demo.sh --drop`. By hand:

```bash
python seed_payments.py --drop # drop, then seed a fresh 300k
Expand Down
31 changes: 31 additions & 0 deletions apps/remote-mcp-perf-triage/setup_demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
#
# Reset, then run the checkout page on :8000. Ctrl+C stops it.
#
# ./setup_demo.sh # drop the index, keep the 300k docs (usual case)
# ./setup_demo.sh --drop # full reseed: fresh 300k, several minutes
#
# Dropping the index the agent created is what restores the slow COLLSCAN. The documents
# stay because nothing in the demo mutates them.
#
# The Advisor trickle is separate: ./trickle.sh start, well before showtime.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"

PY=${PY:-.venv/bin/python}
[[ -x "$PY" ]] || PY=python3

case "${1:-}" in
"") echo "==> Dropping the demo index (restores the slow COLLSCAN)"
"$PY" seed_payments.py --drop-index ;;
--drop) echo "==> Full reseed — several minutes"
"$PY" seed_payments.py --drop
echo "! Cache is cold and the Advisor recommendation reset; allow ~15-30 min of trickle" ;;
*) echo "ERROR: unknown option '$1' (only --drop)" >&2; exit 1 ;;
esac

pgrep -f 'generate_load\.py' >/dev/null \
|| echo "! Advisor trickle NOT running — ./trickle.sh start (allow ~15-30 min)"

echo "==> Checkout page: http://127.0.0.1:8000 (Ctrl+C to stop)"
exec "$PY" checkout_app.py
74 changes: 74 additions & 0 deletions apps/remote-mcp-perf-triage/trickle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
#
# The Performance Advisor trickle — generate_load.py, backgrounded.
#
# ./trickle.sh start # kills any existing trickle, then starts one fresh
# ./trickle.sh stop
# ./trickle.sh status
#
# Separate from setup because it runs on a different clock: the Advisor takes ~15-30 min
# to first surface its recommendation, and only holds it while slow queries keep recurring
# AND the index is still absent. Start it well before showtime and leave it up across demo
# runs. Stop it at end of day.
#
# Watch it with: tail -f load.log
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"

PY=${PY:-.venv/bin/python}
[[ -x "$PY" ]] || PY=python3

# pgrep exits 1 with no matches, which would abort the script under `set -e`.
pids() { pgrep -f 'generate_load\.py' | tr '\n' ' ' || true; }

kill_all() {
local p
p=$(pids)
[[ -z "$p" ]] && return 0
echo "Killing existing trickle: $p"
kill $p 2>/dev/null || true
sleep 2
p=$(pids)
[[ -n "$p" ]] && kill -9 $p 2>/dev/null || true
return 0
}

case "${1:-}" in
start)
kill_all
nohup "$PY" generate_load.py >>load.log 2>&1 &
pid=$!
sleep 2
if ! kill -0 "$pid" 2>/dev/null; then
echo "ERROR: exited immediately — last lines of load.log:" >&2
tail -5 load.log >&2
exit 1
fi
echo "Started (pid $pid). Allow ~15-30 min, then confirm with"
echo "atlas-get-performance-advisor: expect { session_id: 1, status: 1 } on ecommerce.payments"
;;
stop)
[[ -z "$(pids)" ]] && { echo "Not running"; exit 0; }
kill_all
echo "Stopped"
;;
status)
p=$(pids)
if [[ -z "$p" ]]; then
echo "NOT running — the Advisor recommendation will go stale. ./trickle.sh start"
exit 1
fi
# Uptime is the thing that matters: the Advisor cares how long this has been
# running, not that it is alive this second.
ps -o pid=,lstart=,args= -p $p | sed 's/^ *//'
# Plain `if`, not a trailing `&&` — as the last command in this branch a false test
# would become the exit status and report a healthy trickle as a failure.
if [[ -f load.log ]]; then
tail -3 load.log | sed 's/^/ /'
fi
;;
*)
echo "Usage: ./trickle.sh {start|stop|status}" >&2
exit 1
;;
esac
Loading