- The Prefect flows in
src/flows/storage/memes.pyorchestrate ingestion from all automated sources:tg_meme_pipeline,vk_meme_pipeline, andig_meme_pipelineETL raw posts, download media, watermark images, push them into the storage bot chat, and hand off tofinal_meme_pipeline.final_meme_pipelineperforms duplicate checks, normalizes captions, and promotes records by callingupdate_meme_status_of_ready_memes.
- Legacy Modal OCR has been removed. The active image analysis system is Describe Memes (see below).
- User uploads arrive via the upload handler, then
uploaded_meme_auto_reviewinsrc/tgbot/handlers/upload/moderation.pydownloads the submission, watermarks it, and sends it to the storage chat. - After preprocessing,
send_uploaded_meme_to_manual_reviewposts the media into the moderator chat (settings.UPLOADED_MEMES_REVIEW_CHAT_ID) with approve/reject buttons. Moderators interact in that chat to complete review. handle_uploaded_meme_review_buttonenforces moderator-only review, updates meme status (OKorREJECTED), handles payouts, and sends outcome notifications back to the uploader. Approved memes automatically receive "like" reactions from both the uploader and reviewer to seed downstream stats.
- Recommendation queues are stored in Redis; see
src/recommendations/meme_queue.pyfor helper utilities.check_queueandgenerate_recommendationsrefill a user's queue when it drops to two items, blending candidate sources via theCandidatesRetrieverandblendhelper.- Cold-start users (<30 memes sent) fall back to
best_uploaded_memes,fast_dopamine, and curated source lists; more engaged users use weighted blends that may include long-tail boosters. - Accepted recommendations are pushed to Redis with
add_memes_to_queue_by_key; consumption pops entries one-by-one.
- User reactions are persisted through
create_user_meme_reaction/update_user_meme_reaction(seesrc/recommendations/service.py). These records drivecalculate_meme_reactions_statsand related counters, which in turn update meme statuses and recommendation eligibility.
- Manual review happens entirely inside the designated Telegram moderator chat. Keep communications and escalations there for traceability.
- Weekly maintenance (Prefect flow health checks, data hygiene jobs, etc.) runs through Prefect deployment definitions. Use Prefect CLI to trigger flows during scheduled operations.
The describe_memes flow (src/flows/storage/describe_memes.py) uses FREE OpenRouter vision models only to extract text and descriptions from meme images.
- Schedule: current deployment runs every 15 minutes, 9 memes/batch (about 864/day)
- Priority: processes recent user uploads first, then most-liked memes (
nlikes DESC) - Storage: writes to
meme.ocr_resultJSONB withcalculated_attimestamp - Monitoring: use
ocr_result->>'calculated_at'to check recency, NOTmeme.created_at - Circuit breaker: auto-pauses after 3 failures in 1 hour
- Quota guard: Redis stops OpenRouter calls after 900 free-model attempts/day (UTC)
- Backpressure strategy: treat free-tier 429s/timeouts as normal; cool down that model in Redis and retry in later scheduled runs
- NEVER add paid models to
VISION_MODELSlist — balance below $0 blocks ALL models (402) - Need $10+ lifetime purchases for 1,000 req/day (otherwise only 50/day)
- Free model rate limit: 20 rpm
describe_memesrefuses model IDs that do not end in:free- See specs/describe-memes.md for full constraints
prefect deployment resume "Describe Memes (OpenRouter Vision)/Describe Memes (OpenRouter)"Before resuming, verify the root cause is fixed (check recent flow run logs).
- Redis, Postgres, and Telegram configuration live in
src/config.py. OPENROUTER_API_KEY— required for describe_memes. Balance must stay >= $0.