01
Slow to review
Listening happens in real time, so review time grows with every call you record.
GENAI · FULL STACK
A full-stack app for reviewing recorded calls. Upload a file or record in the browser, and it returns a transcript, sentiment, emotion, toxicity, intent and a 0 to 100 compliance score, then checks your own rules and can send alerts and scheduled reports. Personal data is redacted before anything is stored.

SEE IT RUN
This is a real run, recorded from the app running on my laptop. The calls are synthetic: ten scripted support calls for a made-up energy company, voiced with macOS text-to-speech and checked against four rules. Tell the caller the call is recorded, never promise a guaranteed result, make no risk-free promises, and stay civil.
In the call the film follows, a customer was charged twice. The models heard a polite, grateful caller and scored the call 99.9 out of 100. The rules heard the agent say a new plan was “guaranteed to lower your bill every month”, and noticed that no one mentioned the recording. The app marked the call critical and posted both findings to a QA webhook as the analysis finished.
Across the ten calls, the rules flagged three. The score flagged none: nine calls scored 99.9 and one 87.8, partly because of a sentiment label bug, since fixed (see below). On a laptop CPU, each analysis took about twice as long as the call itself.
THE GAP
Most of a compliance review is listening: what was said, whether anyone got abusive, whether a required phrase was said or a banned one slipped out. It is slow, and the result depends on who is listening.
I wanted to see how much of that first pass a chain of open models could handle, and to turn the rules into something a team writes down once and a system checks every time.
01
Listening happens in real time, so review time grows with every call you record.
02
A policy written in a document cannot flag anything. Someone has to apply it, call by call.
03
Even when a problem is found, someone still has to write it up and tell the right people.
WHAT IT DOES
A FastAPI backend transcribes the call with Whisper, runs it through a chain of open models, checks it against rules stored in the database, and saves the result to SQLite. A React and TypeScript front end shows the score, transcript and timelines, with logins, roles, teams, comments and tags so several reviewers can work on the same calls. Alerts, webhooks, emails and scheduled reports carry the findings to people who are not looking at the screen. Personal data is replaced with placeholders straight after transcription, and every sign-in and change lands in an audit log.
Whisper transcribes and detects language. Open models score sentiment, emotion and toxicity, classify intent zero-shot, pull topics and summarize.
Sentiment, emotion and toxicity combine into a 0 to 100 compliance score. Below 70 raises a warning, below 50 a critical alert.
Regex, keyword, sentiment, emotion, toxicity and score-threshold rules live in the database, each with a severity, and can be tested first.
Alerts can fire webhooks and emails. Daily, weekly or monthly reports go out by email, with PDF, CSV and JSON exports.
Card numbers, Social Security numbers, emails, phone numbers and similar details are replaced in the transcript before anything analyzes, stores or sends it. Recordings are deleted as soon as the analysis ends, and optional retention limits remove old analyses and audit entries.
Every route needs a signed-in user with the right role. There are no default passwords, the first sign-in forces a new one, and rate limits and an audit log cover every change.
HOW IT WORKS
Drop in an audio file or record straight from the browser microphone.
Whisper, small model by default, transcribes the call and detects its language. Personal details are replaced with placeholders before anything else sees the text.
Open models score sentiment, emotion and toxicity, then add intent, topics, summary and action items.
SHAP highlights which words pushed the toxicity score up.
Your stored rules and the default thresholds run against the transcript and scores.
Alerts trigger any configured webhooks and emails, and the analysis is saved.
Reviewers see the score, transcript and timeline, then comment, tag, compare or export.
SYSTEM DESIGN
A single FastAPI endpoint runs every analysis in sequence on the uploaded file. Each step is wrapped on its own, so one failing model leaves a gap in the report instead of failing the whole request.
Input
React front end
upload a file or record in the browser
API
FastAPI /analyze_audio
one request runs the whole chain
Auth
JWT, bcrypt, roles and permissions, rate limits, an audit log
Models
Whisper
transcript, timestamps and language
PII redaction
card numbers, SSNs, emails and phones replaced
Transformers + Detoxify
sentiment, emotion, toxicity, intent, summary
SHAP + LDA
word attributions and topics
Rules and alerts
Rule engine
regex, keyword and threshold rules from the database
Notifications
webhooks and SMTP email
Storage and output
SQLite via SQLAlchemy
analyses, rules, users and teams
APScheduler
daily, weekly and monthly emailed reports
Exports
PDF, CSV and JSON
RESULTS
0–100
compliance score per call, with warning and critical alerts below 70 and 50
7
rule types a team can define, from regex and keywords to score thresholds
70
API routes, from analysis to users, teams, webhooks, schedules and the audit log
16
pages in the React and TypeScript front end
This is an application, not a benchmark: the repo has no accuracy measurements, so these are counts from the code.
WHAT IT TAUGHT ME
Looking back at the code, the feature list got ahead of it in places. The speaker turns are a placeholder: the audio is cut into two-second windows and each window is labeled by its spectral centroid, which is not real diarization. The sentiment model returned LABEL_0 to LABEL_2 while the score expected NEGATIVE, NEUTRAL and POSITIVE, so sentiment never moved a score or raised an alert. Custom rules were Python expressions run through eval, on routes that needed no login, behind a default admin password. Those parts are fixed: sentiment now counts, rules go through a small evaluator that can't run code, every route needs a signed-in user with the right role, there are no default credentials, and personal data is redacted before anything is stored. The next version still needs a real diarization model, and a set of labeled calls to calibrate the score.
BUILT WITH
Like what you see?
Thanks for reading. There's more where this came from.