Spaces:
Sleeping
title: DECLARE User Study
emoji: ποΈ
colorFrom: blue
colorTo: green
sdk: docker
app_port: 7860
pinned: false
DECLARE User Study
Blind comparison of three ASR conditions (BASELINE / VANILLA-FT / DECLARE) on
real spoken FairSpeech-style commands. Single-file Flask app (app.py),
matching the flat repo layout used elsewhere (RailVaani), with the frontend in
templates/ + static/.
Participants record themselves speaking a prompted command via the browser's native microphone API; audio is routed blindly to one of three ASR conditions (Latin-square rotation per session); a rule-based intent/entity extractor summarises what the system understood; the participant judges intent-correctness and (if applicable) entity-correctness separately. After all trials, a validated 4-item UMUX usability questionnaire is shown. Model identity is never revealed during the session.
Folder structure
app.py -- everything: Flask routes, NLU, routing, logging, ASR
templates/index.html -- page shell
static/style.css -- styling
static/app.js -- consent form, MediaRecorder, all fetch() calls
data/sample_commands.csv -- 38 stratified FairSpeech commands
checkpoints/ -- put your .nemo files here
requirements.txt
Dockerfile
README.md
Important constraint: session state lives in an in-memory Python dict in
app.py (SESSIONS). This assumes a single worker process. Do not run
behind multiple gunicorn/Flask workers without first moving session state to
a shared store (Redis, a database) β otherwise a participant's session can
"disappear" if a later request lands on a different worker.
Before deploying with real models
The app ships in MOCK mode by default so the full flow β consent, recording, routing, judgement, UMUX β can be tested without any model weights. To run real inference:
- Uncomment the
torch/nemo_toolkit[asr]lines inrequirements.txt. - Add your
VANILLA_FTandDECLARE.nemocheckpoints undercheckpoints/. - Set Space secrets/variables if you don't want to hardcode paths:
BASELINE_MODEL_ID(default:stt_en_conformer_ctc_small_ls)VANILLA_FT_CKPTDECLARE_CKPT
Audio pipeline
Browser records via MediaRecorder (typically webm/opus) β app.py saves the
raw upload β converts to 16kHz mono WAV via ffmpeg β runs ASR β deletes
both the raw upload and the WAV file. The WAV deletion happens inside
transcribe()'s own finally block (so it happens even if inference fails);
the raw upload is deleted separately right after, in the route handler.
Results
results/trial_log.csvβ per-trial ASR output + intent/entity correctnessresults/participants.csvβ demographics + consent (keyed by session ID)results/umux_responses.csvβ usability questionnaire responses + score
HF Spaces' local disk is ephemeral β sync these to a private HF Dataset repo regularly if running more than a quick pilot.
Analysis
Join trial_log.csv back to its (unblinded) condition column and run:
- Friedman test across the three conditions (per-participant correctness rate,
computed separately for
intent_correctandentity_correctβ filter or stratifyentity_correct == "not_applicable"rows out of that specific test) - Wilcoxon signed-rank, pairwise (DECLARE vs. VANILLA-FT is the key comparison)
- Disaggregate both by the demographic columns in
participants.csvfor the fairness question