A CBT-inspired mobile app that detects cognitive distortions in a user's own words as they talk with an AI companion. It recognises when they return to a thought they've had before and turns those patterns into insights, using three services and a custom NLP pipeline.
Project media coming soon
Screenshots and demo videos for this case study will appear here.
MindAware is a reflective companion, not a diagnostic tool. As users talk through what is on their mind, an NLP service tags nine types of cognitive distortion directly in their text. It notices when they return to a thought they have had before and gently prompts reflection. Over time, an insights dashboard shows recurring themes, mood correlations, emotion trends and tentative core beliefs that the user can confirm or reject.
General-purpose LLMs reply to messages, but they don't track how someone thinks over weeks. The system needed explainable pattern detection on sensitive text, long-term semantic memory and firm safety boundaries. It also needed an evaluation method honest enough to show when a classifier isn't good enough.
An Expo and React Native client talks to an Express 5 and Prisma API on Neon Postgres with pgvector, which calls a stateless FastAPI NLP service. That service pairs an LLM classifier (gpt-oss-120b on Groq, in JSON mode) with a rule-based fallback. It uses MiniLM sentence embeddings for repetition and memory, silhouette-tuned K-Means for themes and a local DistilRoBERTa model for emotion. The API orchestrates each request, persists the results and generates the reply.
The result is a complete research prototype: 39 endpoints across three services, 928 passing backend and frontend test cases, a 318-row human-verified evaluation set and full academic documentation.
Feature modules follow a types → store → service pattern. Chat highlights distortions using character offsets, and the insight dashboards are built with victory-native.
Handles authentication, AES-256 encryption of messages at rest, the message pipeline, insights, mood, the article library, search and notifications, behind a routes → controllers → services → repositories structure.
Stateless /analyze, /cluster and /embed-backfill endpoints covering distortion tags, repetition scores, safety verdicts, embeddings and emotion.
Stores 384-dimensional message embeddings and a retrieval (RAG) knowledge base, queried by cosine nearest-neighbour search.
The hosted LLM handles classification, safety confirmation and replies. Embeddings and emotion run locally on CPU so they use no API tokens.
For each message, the API encrypts it, sends it for analysis and stores each distortion with its exact character span. It then searches the user's entire history with pgvector. If at least two earlier messages exceed 0.75 similarity, the reply notes that the user has had this thought before. The system prompt is assembled from those signals, and the client receives the reply, tags, title and memory in a single response. An NLP outage never breaks the chat, because the API falls back to a supportive reply.
A cheap keyword pre-filter runs first, so harmless messages never trigger an LLM call. Only a match escalates to an LLM check that separates genuine first-person risk from figurative language. If that check errors, the message is treated as a crisis rather than as safe. A confirmed crisis skips all analysis and switches the reply to a de-escalation prompt with helpline resources.
I built an evaluation harness with 318 labelled rows, including 92 hard negatives, phrases that look like distortions but are negated, quoted or hypothetical. It reports per-type F1 and a hard-negative accuracy score. The keyword baseline reached a macro-F1 of 0.223 and scored zero on inference-heavy types, which was the evidence for switching to an LLM classifier. LLM predictions are cached so runs can resume, and the harness refuses to publish partial results.
Message content is encrypted with AES-256-CBC using a fresh initialisation vector for every message. Refresh tokens and email codes are stored only as SHA-256 hashes, and logout revokes every session. Google ID tokens are verified on the server. Users can export all their data as JSON or delete their account, which removes everything linked to it.
Emotion classification and embeddings run locally, so they cost nothing per call. Core-belief inference runs only once there is enough evidence: at least 15 distortion tags across three or more types in 90 days, with a 24-hour cooldown. The user must confirm each belief, and rejected beliefs are never suggested again.