Mobile / TypeScript

Building an Offline NLP Engine in Pure TypeScript

May 2026 8 min read
NLP Engine

MindSync needed a chatbot — something that lets users log food and habits conversationally. The obvious solution is to hit an API. Send the string to OpenAI, get structured data back. The problem: MindSync is fully offline. No internet. No API keys. So I built the understanding layer myself.

What "NLP" Actually Means at This Scale

I'm not building a transformer model. What I needed: given free-text input, extract intent and entities. Intent is what the user wants to do — log food, log a habit, ask a question. Entity is the actual data — what food, what quantity. For that, regex patterns and rule trees get you surprisingly far.

Normalize, Detect, Extract

The engine follows three steps. First, normalize: strip and lowercase the input, expand contractions, and replace synonyms so "ate", "had", and "consumed" all trigger the same food-log path. This single step eliminated ~40% of edge cases in testing.

Second, intent detection: each intent has an array of regex triggers. The engine runs through them in priority order and returns the first match. Simple rules you understand beat a black box you don't, especially when you're debugging at 2am.

Third, entity extraction: number-word patterns handle quantities ("two eggs", "a glass of", "200ml"). Named food items match against a local dictionary. Missing quantities default to a standard serving size.

The hardest part was not building the parser — it was deciding what to do when it fails. Failing silently is worse than asking the user to rephrase. I added a low-confidence fallback that replies: "I didn't catch that — could you rephrase?"

What I'd Do Differently

The biggest issue is maintenance. Every new supported food or habit requires updating multiple hardcoded arrays. I should have built a single JSON config file from the start that the engine reads at runtime. That would have made testing and expansion far faster — and it's the first thing I plan to refactor in v2.

All Posts Next: Dual Themes