feat(tasks): add vision task scheduler for trust photo analysis #14

Merged
pyr0ball merged 4 commits from feature/shared-task-scheduler into main 2026-04-03 21:41:12 -07:00
Owner

Summary

  • Adds app/tasks/runner.py wiring Snipe to circuitforge_core.tasks.scheduler
  • Task type: trust_photo_analysis (2.0 GB VRAM budget) — downloads listing photo, base64-encodes it, calls LLMRouter with vision payload, writes JSON result to trust_scores.photo_analysis_json
  • Vision system prompt requests structured JSON: is_stock_photo, visible_damage, authenticity_signal, confidence
  • LLMRouter imported from circuitforge_core.llm (Snipe has no scripts/ directory)
  • Migration 002: background_tasks table (first migration after 001_init.sql)
  • FastAPI lifespan in api/main.py starts/stops the scheduler on app lifecycle

⚠️ Depends on Circuit-Forge/circuitforge-core#2 (shared task scheduler)

Test plan

  • App starts and scheduler initialises without error
  • trust_photo_analysis task enqueues when a listing has a photo URL
  • LLMRouter receives base64 image payload
  • Result JSON written to trust_scores.photo_analysis_json
  • Scheduler shuts down cleanly on app teardown
## Summary - Adds `app/tasks/runner.py` wiring Snipe to `circuitforge_core.tasks.scheduler` - Task type: `trust_photo_analysis` (2.0 GB VRAM budget) — downloads listing photo, base64-encodes it, calls `LLMRouter` with vision payload, writes JSON result to `trust_scores.photo_analysis_json` - Vision system prompt requests structured JSON: `is_stock_photo`, `visible_damage`, `authenticity_signal`, `confidence` - `LLMRouter` imported from `circuitforge_core.llm` (Snipe has no `scripts/` directory) - Migration 002: `background_tasks` table (first migration after `001_init.sql`) - FastAPI lifespan in `api/main.py` starts/stops the scheduler on app lifecycle > ⚠️ **Depends on Circuit-Forge/circuitforge-core#2** (shared task scheduler) ## Test plan - [ ] App starts and scheduler initialises without error - [ ] `trust_photo_analysis` task enqueues when a listing has a photo URL - [ ] LLMRouter receives base64 image payload - [ ] Result JSON written to `trust_scores.photo_analysis_json` - [ ] Scheduler shuts down cleanly on app teardown
pyr0ball added 1 commit 2026-03-31 10:43:25 -07:00
Wires circuitforge_core.tasks.scheduler into Snipe. Adds trust_photo_analysis
background task: downloads primary listing photo, calls LLMRouter with vision
capability, writes result to trust_scores.photo_analysis_json (Paid tier).
photo_analysis_json column already existed in 001_init.sql migration.
pyr0ball added 1 commit 2026-03-31 17:00:08 -07:00
- Rename 002_background_tasks.sql → 007_background_tasks.sql to avoid
  collision with existing 002_add_listing_format.sql migration
- Add CREATE UNIQUE INDEX on trust_scores(listing_id) in same migration
  so save_trust_scores() can use ON CONFLICT upsert semantics
- Add Store.save_trust_scores() — upserts scores keyed by listing_id;
  preserves photo_analysis_json so runner writes are never clobbered
- runner.py: replace raw sqlite3.connect() with get_connection() throughout
  (timeout=30 + WAL mode); fix connection leak in insert_task via try/finally
- _run_trust_photo_analysis: read 'user_db' from params to write results to
  the correct per-user DB in cloud mode (was silently writing to wrong DB)
- main.py lifespan: use _shared_db_path() in cloud mode so background_tasks
  queue lives in shared DB, not _LOCAL_SNIPE_DB
- Add _enqueue_vision_tasks() and call it after score_batch() — this is the
  missing enqueue call site; gated by features.photo_analysis (Paid tier)
- Test fixture: add missing 'stage' column to background_tasks schema
pyr0ball added 1 commit 2026-04-03 19:09:03 -07:00
**Scammer blocklist**
- migration 006: scammer_blocklist table (platform + seller_id unique key,
  source: manual|csv_import|community)
- ScammerEntry dataclass + Store.add/remove/list_blocklist methods
- blocklist.ts Pinia store — CRUD, export CSV, import CSV with validation
- BlocklistView.vue — list with search, export/import, bulk-remove; sellers
  show on ListingCard with force-score-0 badge
- API: GET/POST/DELETE /api/blocklist + CSV export/import endpoints
- Router: /blocklist route added; AppNav link

**Migration renumber**
- 002_background_tasks.sql → 007_background_tasks.sql (correct sequence
  after blocklist; idempotent CREATE IF NOT EXISTS safe for existing DBs)

**Search + listing UI overhaul**
- SearchView.vue: keyword expansion preview, filter chips for condition/
  format/price, saved-search quick-run button, paginated results
- ListingCard.vue: trust tier badge, scammer flag overlay, photo count
  chip, quick-block button, save-to-search action
- savedSearches store: optimistic update on run, last-run timestamp

**Tier refactor**
- tiers.py: full rewrite with docstring ladder, BYOK LOCAL_VISION_UNLOCKABLE
  flag, intentionally-free list with rationale (scammer_db, saved_searches,
  market_comps free to maximise adoption)

**Trust aggregator + scraper**
- aggregator.py: blocklist check short-circuits scoring to 0/BAD_ACTOR
- scraper.py: listing format detection, photo count, improved title parsing

**Theme**
- theme.css: trust tier color tokens, badge variants, blocklist badge
pyr0ball added 1 commit 2026-04-03 21:35:57 -07:00
pyr0ball merged commit d5419d2b1b into main 2026-04-03 21:41:12 -07:00
Sign in to join this conversation.
No reviewers
No labels
backlog
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/snipe#14
No description provided.