Tags: danmunz/docent
Tags
Atmosphere triptych — 3 curated picks with variety engine (#76) * feat: atmosphere triptych — 3 curated picks with variety engine Replaces single-recommendation Atmosphere modal with a 3-card triptych: Backend: - New ATMOSPHERE_PROMPT requesting 3 ranked picks with lateral-thinking guidance - Recommendation history file (atmosphere_history.json) tracking picks over time - Inverse-frequency weighted pre-filter samples ~20 candidates, avoiding repeats - random.shuffle() eliminates insertion-order bias in LLM context - temperature 0.9 and max_tokens 600 for atmosphere calls only - Fallback padding ensures 3 recommendations even if LLM returns fewer Frontend: - 3-card triptych layout (820px max-width, responsive stack at <=600px) - First card marked Curators pick with accent top-border - Staggered reveal animation (150ms intervals) - Each card: thumbnail, wiki-linked title, curator note, vibe pills, Display button - Explored N of M counter below cards - atmosphereRetry() excludes all 3 picks at once - localStorage persistence with 24h TTL for exclusion list - closeAtmosphere() no longer clears exclusions Tests: - Updated test_full_pipeline for 3-recommendation response schema - New test_fallback_padding and test_history_persistence - Updated XSS test for safeTitle variable in pick cards Closes #65 * fix: deduplicate repeated content_ids in atmosphere recommendations Track used_ids during LLM result parsing to skip duplicate content_ids before the padding step. Prevents duplicate cards when the model returns the same artwork more than once. Adds test_dedup_repeated_ids to verify 3 unique picks are returned even when the LLM repeats an ID.
feat: guided Vision API setup with key validation (#71) - Add VisionApiError with structured error parsing (extracts Google's project-specific enable URL from 403 responses) - Add POST /api/ai/test-vision endpoint for key validation - Replace 'Get a key' hint with numbered setup guide (Enable API → Create key → Paste) and inline Test button with ✓/✗ feedback - Propagate vision_error in analysis metadata; show warning in UI when Vision silently fails - Auto-test after saving a new key; support test-before-save flow - 7 new tests for test-vision endpoint
fix: non-blocking thumbnail prefetch and WebSocket staleness recovery (… …#67) * test: add diagnostic and reproduction tests for issue #67 thumbnail failures Add 20 tests across two files that identify and reproduce the root cause of thumbnail loading failures on large catalogs (500+ artworks). Root cause: client-server timeout race condition. The server-side individual fallback (PR #69) takes 53s+ for a batch of 20 IDs when the TV is struggling, but the frontend's AbortController timeout is 30s. The client always aborts before receiving the response, never sees the fallback:true flag, so the progress bar never appears. Three retries create a ~96s loop ending in 'Tap to retry' on all thumbnails. test_issue67_diagnosis.py (14 tests): - Thumbnail key matching with samsungtvws format - Individual fallback cascade timing (20 TV calls per failed batch) - TV operation timeout and D2D socket orphaning - Lock starvation during concurrent fallback operations - Large catalog scenario (63 TV hits per batch of 20) - Client timeout race: proves 1 failing ID = 58s worst-case (>30s) test_reproduce_nitrowolf.py (6 tests): - Server response exceeds client timeout (53s vs 30s) — matches logs - Click-to-retry reads from disk cache (2.6ms) — matches user report - Progress bar never shown (client aborts before response) - Three-attempt retry loop (96s wall time → 'Tap to retry') - Concurrent batches compounding the timeout problem - Settings endpoint blocked by TV lock during fallback Uses 50x time scaling for fast test execution while preserving the timing relationships that cause the failures. Refs: #67, #68, #69 * fix: address Codex review feedback on PR #70 - Mark reproduction test classes with @pytest.mark.xfail(strict=False) so a production fix for #67 won't break CI. Tests currently show as XPASS (bug still exists); once fixed they'll become XFAIL (expected). - Replace time.sleep(10) in hanging_fn with threading.Event.wait(2) and cancel.set() after assertions, so the executor thread exits promptly instead of stalling the suite for 10s. * fix: non-blocking background prefetch for thumbnail fallback (#67) Replace synchronous individual thumbnail fallback (which blocked 53s+ for large catalogs) with asyncio.create_task() background prefetch. Server changes: - Add _prefetch_thumbnails() background task that fetches thumbnails individually via _tv_op() and caches to disk - Add _thumb_prefetch_in_progress dedup set to avoid redundant fetches - get_thumbnails_batch() now returns immediately with fallback=True and all uncached IDs as missing, spawning background prefetch Client changes: - In fallback mode, retry up to 8 times at 3s intervals (vs 3 retries with escalating backoff) to pick up newly cached thumbnails Test updates: - conftest: reset _thumb_prefetch_in_progress and _tv_lock per test - test_api_endpoints: verify immediate response + background cache - test_issue67_diagnosis: update 5 diagnostic tests for new async behavior (fast response, background individual calls, disk cache) * fix: proactively reconnect stale TV WebSocket connections Samsung Frame WebSocket connections go stale after ~30-60s of inactivity, causing BrokenPipeError when the user interacts with the TV after browsing Atmosphere results or other idle periods. - Track _tv_last_used timestamp, updated after each successful _tv_op - _ensure_tv_connection checks idle time against TV_CONN_MAX_IDLE (30s) - Stale connections are proactively closed and reopened instead of waiting for BrokenPipeError on the first attempt - Reset _tv_last_used in test fixtures for proper isolation * refactor: address PR review comments from Copilot and Codex - Fix misleading log: 'falling back to individual' → 'scheduling background prefetch' (Copilot on server.py:524) - Fix misleading comment: 'shorter delays' → 'fixed 3s interval' with note about normal escalating backoff (Copilot on index.html:2879) - Replace fixed sleep(0.2) with bounded polling loop in fallback tests to avoid CI flakiness (Copilot on test_api_endpoints.py:395,424) - Rename test_526 → test_524 to match docstring catalog size (Copilot on test_issue67_diagnosis.py:281)
feat: persistent TV connection, /health endpoint, corruption recovery Closes #67, #57, #63, #19, #47 - Persistent WebSocket connection (11 connections → 2) - Removed concurrent socket race (_fetch_thumbnails_sync) - /health endpoint with Docker HEALTHCHECK (503 on errors) - JSON corruption recovery with _safe_load_json - Jump-to-current-artwork FAB - AI settings inline help text