radio: session log update — index UI exposes classifier filters

Backend min_score/exclude_banter wired through to HTML index. Adds
score badges (1-5 red->green), topic_class pills, dim styling on
banter rows. Live on http://172.16.3.20:8765/. Synced to portable
repo. pscp ENOSPC quirk worked around by plink-stdin streaming.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 06:07:00 -07:00
parent b9af34fbd8
commit 6239f9fc3a

View File

@@ -250,3 +250,109 @@ The SQLite connection on the container side is `mode=ro` URI — picks up fresh
- **Laptop install** is a clone + 2 shell scripts; untested on the actual 5070 (will validate next week)
- **ClaudeTools commits:** `5e3b1a2` (this session's main.py change)
- **Untested edge cases:** offline behavior (planes, no Tailscale), curl with HTTP/2 to /api/db.sqlite (was tested with HTTP/1.1)
---
## Update: 06:05 — Index UI exposes classifier filters
User asked to wire the new classifier fields into the search UI. The
backend already supported `min_score` and `exclude_banter` query params
(commit `5e3b1a2`); this update brings them into the HTML index and adds
visible quality indicators on Q/A hits.
### Update Summary
Edited `INDEX_HTML` in `server/main.py` to add two filter controls and
score badges. Verified locally via `uvicorn` on `127.0.0.1:8866` against
the classifier-populated DB (no-filter, `min_score=4`, and
`exclude_banter=true` modes all behaved correctly). Hit an unexpected
`No space left on device` from `pscp` despite Jupiter having 37 TB free
on `/mnt/user`; bypassed by streaming the file through plink stdin
(`plink ... "cat > /path" < local_file`). md5 verified byte-identical.
Container rebuilt via `docker compose up -d --build`. Synced the same
`main.py` to the portable repo so the laptop UI stays in sync.
### What changed in the UI
- **`min score` select** — values: any, 2+, 3+, 4+, 5. Default `any` to
preserve old search behavior. Filters surface 1,096 mid-and-above
pairs at `3+` or 523 useful pairs at `4+`.
- **`hide banter` checkbox** — when checked, drops the 606 rows with
`is_banter=1`.
- **Score badge per Q/A hit** — small color-coded number (1=red, 5=green)
next to each hit's metadata line. Title attribute shows
`usefulness N/5` on hover.
- **Topic class tag** — small gray pill showing `computer-help`,
`banter`, `off-topic`, `promo`, or `unclear`.
- **Dimmed rendering** — hits with score 1-2 or `is_banter=true` render
at 55% opacity. Visible but visually de-emphasized so good hits stand
out at a glance.
- **`escapeHtml` helper** — defensive XSS guard on `caller_name` and
`title` (transcript-derived strings).
### Key Decisions (this update)
- **Default filter "any"** — preserves prior search habits and saved
URLs. Mike opts into filtering when needed rather than being forced
into a curated view.
- **`URLSearchParams` instead of string concat** — only emits
`min_score=` / `exclude_banter=` when non-default, keeping URL bar
clean for the common case.
- **Color-coded badge with both score AND topic tag** — score is
numeric/comparable; topic tag is categorical and explains *why* a
score is what it is. Both together make the classifier's reasoning
visible at a glance without forcing a click.
- **Dim instead of hide for low-quality hits** — keeps everything
visible by default; the filter controls are the explicit "hide" lever.
- **Used `plink "cat > path"` instead of pscp** for the deploy when
pscp failed — faster than diagnosing the underlying scp/shfs issue
and gets the job done deterministically.
### Problems Encountered (this update)
- **pscp ENOSPC despite 37 TB free** — `pscp main.py` failed with
`No space left on device` on two retries. df showed 37 TB free on
`/mnt/user`, df -i showed inodes fine. Workaround:
`plink ... "cat > /path/main.py" < local_main.py`. md5sum confirmed
byte-identical post-transfer. Likely Unraid shfs cache-pool churn or
an issue with overwriting an in-use file from inside a container's
mount. Worth understanding eventually but didn't block the deploy.
- **plink output buffering on chained docker commands** — long
`docker compose up -d --build` runs hung from Bash's run_in_background
view (output file stayed empty for minutes). Foreground sync run with
the same command worked instantly. Same pattern observed yesterday.
Workaround: don't background long plink runs; just block.
### Files Changed (this update)
| Path | Change |
|---|---|
| `projects/radio-show/audio-processor/server/main.py` | +51 / -4 — INDEX_HTML gained controls + badge styles + topic tag + escapeHtml + dim-class JS rendering |
| `c:/Users/guru/radio-archive-portable/server/main.py` | Same diff, synced from upstream |
### Commits (this update)
| Repo | SHA | Branch |
|---|---|---|
| ClaudeTools | `b9af34f` | main |
| radio-archive-portable | `1d6c795` | main |
### Live verification
```
$ curl -s http://172.16.3.20:8765/ | grep -cE "min_score|exclude_banter|badge.s5|topic_class"
10
$ curl -so /dev/null -w "%{size_download}\n" http://172.16.3.20:8765/
5757 # was 4040 before
$ curl -s 'http://172.16.3.20:8765/api/search?q=BIOS&kind=qa&min_score=4&limit=2' \
| python -c "import sys,json; d=json.load(sys.stdin); print('hits:', len(d['qa']))"
hits: 2 # both score=5, topic_class='computer-help'
```
### Status at update end
- UI controls live on http://172.16.3.20:8765/ and on the portable repo
- Backend filters working (verified end-to-end)
- Untouched: HTML still has no per-hit deep-link to `/api/episodes/{id}`
(clicking a hit doesn't navigate). Future enhancement.
- Pending: laptop validation (still next week's task)