Danialebrat's picture
deploying the new UI
dcdaf35

A newer version of the Streamlit SDK is available: 1.56.0

Upgrade
metadata
title: AI Messaging System
emoji: 🎢
colorFrom: blue
colorTo: gray
sdk: streamlit
sdk_version: 1.50.0
python_version: 3.9
app_file: app.py

AI Messaging System v3 β€” Visualization Tool

A Streamlit tool for reviewing and evaluating AI-generated push notification messages. All data is read from a single output table β€” no joins. Reviewers filter messages by brand, campaign, and scenario, inspect user context stored at generation time, and flag any message that has issues. Rejections are persisted to Snowflake. No action on a message means it is good.

This tool does not generate messages. It is purely for reviewing messages already produced by the v3 pipeline.


Purpose

  • Pre-fetch the most recent 150 messages per campaign on login (single query, no joins)
  • Filter instantly by brand, campaign, and scenario β€” all client-side, no extra DB queries
  • View the user context captured at generation time (streaks, profile, previous messages, last interacted content)
  • Reject messages with a structured reason β€” persisted to Snowflake immediately
  • Track rejection counts per review session in the sidebar

Directory Structure

visualization/
β”œβ”€β”€ app.py                          # Entry point: login + data pre-fetch on authentication
β”œβ”€β”€ pages/
β”‚   └── 1_Message_Viewer.py         # Message cards, filters, user context, rejection UI
β”œβ”€β”€ utils/
β”‚   β”œβ”€β”€ auth.py                     # Login / session authentication
β”‚   β”œβ”€β”€ snowflake_client.py         # Snowflake reads (single table) and feedback writes
β”‚   β”œβ”€β”€ feedback_manager.py         # In-session rejection state + Snowflake persistence
β”‚   β”œβ”€β”€ theme.py                    # Brand colours, campaign labels, scenario definitions
β”‚   └── __init__.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .env                            # Credentials (not committed)
β”œβ”€β”€ .env.example
└── README.md

Getting Started

1. Install dependencies

pip install -r requirements.txt

2. Configure credentials

Copy .env.example to .env and fill in your Snowflake credentials and the app token:

SNOWFLAKE_USER     = ...
SNOWFLAKE_PASSWORD = ...
SNOWFLAKE_ACCOUNT  = ...
SNOWFLAKE_ROLE     = ACCOUNTADMIN
SNOWFLAKE_DATABASE = RECSYS_V3
SNOWFLAKE_WAREHOUSE= COMPUTE_WH
SNOWFLAKE_SCHEMA   = PUBLIC
APP_TOKEN          = ...

3. Run the app

cd ai_messaging_system_v3/visualization
streamlit run app.py

Authentication

Login requires an email from the authorised list and a shared access token (APP_TOKEN in .env). Authorised emails are defined in utils/auth.py.


Snowflake Tables

Input (read-only) β€” single table, no joins

All data needed for display and filtering is stored at message generation time in one table.

MESSAGING_SYSTEM_V2.GENERATED_DATA.DAILY_PUSH_MESSAGES

Column Type Description
USER_ID NUMBER Target user
DETECTED_BRAND VARCHAR Brand: drumeo, pianote, guitareo, singeo, playbass
BRANCH VARCHAR Campaign routing key (mirrors CAMPAIGN_NAME)
MESSAGE VARCHAR JSON string {"header": "...", "message": "..."} β€” parsed into header + body
PLATFORM VARCHAR Delivery platform (e.g. push)
CAMPAIGN_NAME VARCHAR dailyPush_dailyStreak, dailyPush_weeklyStreak, dailyPush_noStreak
TIMESTAMP TIMESTAMP Generation timestamp
RECOMMENDATION VARCHAR Recommendation type; for_you means no specific recommendation (used for scenario detection)
RECOMMENDED_CONTENT_ID NUMBER Content ID linked in the message (if any)
FIRST_NAME VARCHAR User's first name at generation time
CURRENT_DAILY_STREAK_LENGTH NUMBER Daily streak at generation time
CURRENT_WEEKLY_STREAK_LENGTH NUMBER Weekly streak at generation time
USER_PROFILE VARCHAR User profile text used for personalisation
PREVIOUS_MESSAGES VARCHAR JSON snapshot of prior messages for this user
LAST_INTERACTED_CONTENT_PROFILE VARCHAR Profile of the last content the user interacted with

Output (written by this tool)

MESSAGING_SYSTEM_V2.UI.V3_FEEDBACKS β€” created automatically on first run.

Column Description
BATCH_ID UUID generated per review session
USER_ID Reviewed user
CAMPAIGN_NAME Campaign of the reviewed message
BRAND Brand of the reviewed user
REJECTION_REASON Category key (see Rejection Reasons)
REJECTION_TEXT Optional free-text note from the reviewer
MESSAGE_HEADER Full header at time of review
MESSAGE_BODY Full body at time of review
MESSAGE_TIMESTAMP When the message was originally generated
REVIEWER_EMAIL Email of the reviewer
TIMESTAMP When the rejection was submitted

Rejections are upserted via MERGE β€” changing the reason on an already-rejected message updates the existing row.

Users with any existing rejection in V3_FEEDBACKS are excluded from the pre-fetch query, so the same message is never shown twice across sessions.


Filters

All filters are applied instantly client-side on the pre-loaded DataFrame β€” no Snowflake query is triggered on filter change.

Filter Source column Notes
Brand DETECTED_BRAND All, Drumeo, Pianote, Guitareo, Singeo, Playbass
Campaign CAMPAIGN_NAME All, Daily Streak, Weekly Streak, No Streak
Scenario RECOMMENDATION Weekly Streak campaign only (see Scenarios)
Only users with previous messages HAS_PREVIOUS Pre-computed at fetch time via COUNT(*) > 1

Campaigns and Scenarios

Campaign Scenario key Display label Detection
dailyPush_dailyStreak β€” β€” No sub-scenarios
dailyPush_weeklyStreak no_practice_this_week No Practice This Week RECOMMENDATION == 'for_you'
dailyPush_weeklyStreak practiced_this_week Practiced This Week RECOMMENDATION != 'for_you'
dailyPush_noStreak β€” β€” No sub-scenarios

Rejection Reason Categories

Key Label
poor_header Poor Header
poor_body Poor Body / Content
grammar_issues Grammar Issues
emoji_problems Emoji Problems
recommendation_issues Recommendation Issues
wrong_information Wrong / Inaccurate Information
tone_issues Tone Issues
similarity Similar To Previous Messages
other Other

Supported Brands

Brand Colour
Drumeo Red #E84545
Pianote Blue #4A90D9
Guitareo Amber #F5A623
Singeo Purple #7B68EE
Playbass Green #3DAA5C

UI Flow

app.py (Login form)
    ↓ authenticated
app.py (Landing)
    β†’ pre-fetches top 150 messages per campaign from DAILY_PUSH_MESSAGES
    β†’ stores result in session_state["all_messages"]
    β†’ shows per-campaign counts
    ↓ navigate to Message Viewer
pages/1_Message_Viewer.py
    Sidebar (instant, no Apply button):
        Brand / Campaign / Scenario / "Only users with prev messages"
        Reload button β†’ clears cache, re-fetches
    Sidebar:
        Session stats: showing Β· rejected Β· reject %

    Main area β€” paginated cards (20 per page):
        Each card:
        β”Œβ”€ Brand badge Β· Campaign Β· User #ID Β· Generated timestamp ──┐
        β”‚                                                             β”‚
        β”‚  Push Notification Preview                                  β”‚
        β”‚  [Header text]                                              β”‚
        β”‚  [Body text]                                                β”‚
        β”‚  Header: N chars Β· Body: N chars                           β”‚
        β”‚                                                             β”‚
        β”‚  β–Ό User Context          (collapsible)                      β”‚
        β”‚    First name Β· Daily streak Β· Weekly streak Β· Profile      β”‚
        β”‚                                                             β”‚
        β”‚  β–Ό Previous Messages     (collapsible)                      β”‚
        β”‚    Parsed from PREVIOUS_MESSAGES column β€” no extra query    β”‚
        β”‚                                                             β”‚
        β”‚  β–Ό Last Interacted Content Profile  (collapsible)           β”‚
        β”‚                                                             β”‚
        β”‚  Feedback column:                                           β”‚
        β”‚    [❌ Reject] β†’ reason dropdown + optional note β†’ Submit   β”‚
        β”‚    Already rejected: shows reason Β· [✏️ Change] [Clear]    β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β†’ Rejections saved to Snowflake on Submit
    β†’ No action = message is considered good

Module Responsibilities

Module Responsibility
auth.py Session authentication, email allowlist, token verification
snowflake_client.py Single-table fetch from DAILY_PUSH_MESSAGES; feedback MERGE/DELETE
feedback_manager.py In-session rejection dict (fast cache), delegates writes to snowflake_client
theme.py Brand colours, campaign labels, scenario definitions and detect_scenario()