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.

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.
Whisper transcribes and detects language. Open models score sentiment, emotion and toxicity, classify intent zero-shot, pull topics and summarize.
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.
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.
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 tokens, bcrypt hashes, roles and permissions
Models
Whisper
transcript, timestamps and language
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 to 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
64
API routes, from analysis to users, teams, webhooks and schedules
14
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 returns LABEL_0 to LABEL_2, but the score's weight table expects NEGATIVE, NEUTRAL and POSITIVE, so sentiment never moves the score. And custom rules are Python expressions run through eval, on routes that need no login, which is fine on a laptop and nowhere else. The next version needs a real diarization model, a tested label map, and rules that cannot run code.
BUILT WITH
Like what you see?
Thanks for reading. There's more where this came from.