Catalyst: A Pure-C# NLP Pipeline That Loads Its Models From NuGet
🚀 Catalyst is a C# Natural Language Processing library built for speed. Inspired by spaCy's design, it brings pre-trained models, out-of-the box support for training word and document embeddings, and flexible entity recognition models.
At a glance
- What is it?
- Catalyst is an MIT-licensed C# NLP library modelled on spaCy, with tokenization, POS tagging, entity recognition and FastText or StarSpace embedding training. Its distinguishing decision is that language data ships as separate NuGet packages and models are lazy loaded through a configurable storage layer.
- Who is it for?
- Adopt Catalyst if your stack is .NET Standard 2.0 or .NET Core and you want tokenization, POS tagging and entity recognition inside the same process as your application, without a Python service. Do not adopt it if you need transformer-grade accuracy, a large published accuracy table, or a model registry you do not control.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Catalyst solves is the Python boundary, not NLP itself
Most .NET teams that need part-of-speech tags or entity spans end up running a Python service next to their application, usually spaCy behind an HTTP or gRPC call. That adds a second runtime, a second deployment artifact and a serialization hop for every document. Catalyst removes that boundary by reimplementing the pipeline in C#. The README describes it as a pure-C# library supporting .NET Standard 2.0, running anywhere .NET Core runs, including ARM. The stated inspiration is spaCy's design, and the feature list mirrors what spaCy users expect: tokenization, part-of-speech tagging, lemmatization, language detection, named entity recognition and word or document embeddings. The intended reader is a .NET developer who has an existing corpus or document stream and wants linguistic annotations in the same process. It is not aimed at researchers who need to fine-tune a transformer, and nothing in the material suggests it competes on accuracy with modern neural taggers.
Tokenization is regex-free by design, and that choice drives the rest of the pipeline
The README states the tokenizer is non-destructive and over 99.9 percent regex-free, with a throughput claim of more than one million tokens per second on a modern CPU. The linked source file is Catalyst/src/Models/Base/FastTokenizer.cs. The regex-free claim is the interesting part: the library avoids the pattern-matching engine for the hot path and relies on character-level scanning instead. That matters for throughput and for predictable latency, since regex backtracking is a common source of tail-latency spikes in text pipelines. The trade-off is that tokenization rules live in code rather than in editable patterns, so unusual scripts or domain-specific token boundaries are handled by whatever the tokenizer implements, not by a configuration file you can adjust. The README does not describe an extension point for custom tokenizer rules, so treat the tokenizer as fixed unless you are willing to fork or patch it.
Entity recognition is three separate mechanisms, not one model
Catalyst does not present a single NER model. The feature list names three distinct approaches with links into the source tree: a gazetteer spotter (EntityRecognition/Spotter.cs), a rule-based PatternSpotter (EntityRecognition/PatternSpotter.cs), and a perceptron-based recognizer (EntityRecognition/AveragePerceptronEntityRecognizer.cs). The perceptron recognizer is a linear model, not a neural one, which sets expectations for accuracy on ambiguous or nested entities. The PatternSpotter is the most concrete of the three in the README, which shows a pattern named Is+Noun built from PatternUnit objects. Each unit constrains either a single token or a run of tokens, and the constraints are token text and part-of-speech tags. In the example, the first unit matches the literal token "is" with PartOfSpeech.VERB, and the second matches one or more tokens tagged NOUN, PROPN, AUX, DET or ADJ. This is a token-and-tag pattern language, so it depends on the tagger being right. If the POS tagger mislabels a word, the pattern silently fails to fire. There is no fuzzy matching described, and no confidence score mentioned for pattern hits.
Models are NuGet packages, and loading goes through a storage abstraction
The distribution model is the part that most distinguishes Catalyst from a Python NLP stack. Language-specific data and models are shipped as separate NuGet packages, discoverable by searching NuGet for catalyst.models. The README states these are trained on Universal Dependencies v2.7. That means the model version and the library version are decoupled: upgrading Catalyst does not automatically upgrade your English or German models, and pulling a newer model package can change tagging output without any change to your code. The README also points at a NuGet search URL rather than a fixed list, so the set of available languages is whatever that search returns at the time you look. I cannot confirm from this material which languages have packages, how large they are, or how frequently they are retrained. Treat language coverage as something to check before you commit, not something the README promises.
Getting a pipeline running takes four steps and one global setting
The README's getting-started example is short. Install the Catalyst NuGet package, then register each language you need, because registration is per-language and the corresponding model package must also be installed. The call is Catalyst.Models.English.Register(). Next, set the storage location, which is a static property: Storage.Current = new DiskStorage("catalyst-models"). The README says models are then lazy loaded either from disk or downloaded from the online repository, so the first run against a cold directory will fetch model files over the network. That is a deployment consideration: an offline or firewalled environment needs the model directory pre-populated. Then the pipeline is created asynchronously with await Pipeline.ForAsync(Language.English), a Document is constructed with its text and language, and nlp.ProcessSingle(doc) annotates it in place. Output is available as JSON via doc.ToJson(). Batch processing uses nlp.Process(docs) over an IEnumerable, and the README frames this as using C# lazy evaluation and native multi-threading, with a generator yielding a thousand documents. Note that the enumeration is lazy, so the work happens as you iterate, not when Process is called.
Embedding training and serialization are where the library expects you to bring data
Catalyst supports training FastText and StarSpace embeddings, and the README marks pre-trained embedding models as coming soon, which means you supply the corpus. The FastText example sets the model type to FastText.ModelType.CBow and the loss to FastText.LossType.NegativeSampling, then calls ft.Train(nlp.Process(GetDocs())) and ft.StoreAsync(). Training consumes the output of the same pipeline used for inference, so tokenization and tagging quality feed directly into embedding quality. Serialization uses MessagePack, and the README shows the stream-based API: a PatternSpotter is constructed, NewPattern is called with a name and a builder delegate, and the model is persisted with await isApattern.StoreAsync(f) into a FileStream opened with File.OpenWrite. Loading reverses it with LoadAsync on a read stream. Storing models as streams rather than fixed paths is useful for blob storage or embedded resources, and it is a genuinely practical detail that many libraries omit. The README does not document a schema version for these binaries, so a Catalyst upgrade that changes the serialized shape could invalidate stored models. Verify that before you build a long-lived model artifact pipeline.
Where Catalyst is the wrong tool, and what to use instead
The clearest limitation is the model class. The named entity recognizer is an averaged perceptron, a linear model, and the README lists no neural tagger. If your task involves nested entities, long-range context, or domain vocabulary that the Universal Dependencies training data does not cover, a transformer-based tagger will do better, and Catalyst is the wrong pick. The alternative that most .NET teams actually choose is spaCy, called from Python through a sidecar process or a REST endpoint. The difference is not just language. spaCy ships a single integrated model artifact per language with published accuracy figures, and its training loop is the primary workflow for most users. Catalyst inverts both: models arrive as independently versioned NuGet packages, and the documented workflows emphasize rule-based and gazetteer entity extraction alongside a linear recognizer. If your entity set is small and well defined, a gazetteer or PatternSpotter in Catalyst can be more predictable than a statistical model, because you can read the rule and know why it matched. If your entity set is open-ended, that same design is a liability. The second alternative, if you only need embeddings and not linguistic annotations, is to skip Catalyst entirely and call a dedicated embedding library or service, since training FastText yourself requires a corpus and time that the README does not help you estimate.
Maintenance cost, licence and what to check before adopting
Catalyst is MIT licensed, which permits commercial and closed-source use with the usual requirement to retain the copyright and permission notice. That is the permissive end of the spectrum, and it means you can vendor or modify the library. The maintenance cost sits in three places. First, model packages are versioned separately from the library, so you need a policy for when to bump catalyst.models packages and a way to detect output changes when you do. Second, Storage.Current is a static global, so a process that needs two different model directories, or that runs tests in parallel with different storage, has to manage that state carefully. Third, the README marks lemmatization, language packages and pre-trained embeddings with a sparkle emoji, and embeddings are explicitly labelled as coming soon, so parts of the feature list are newer than others. The repository was last pushed in August 2026 and no releases were retrieved, so check the NuGet package page for the current version and its publish date rather than assuming the repository state matches the published artifact. The related projects named in the README, an HNSW package on NuGet and a UMAP implementation on GitHub, are separate dependencies you would evaluate on their own terms.
Editorial conclusion
Adopt Catalyst if your stack is .NET Standard 2.0 or .NET Core and you want tokenization, POS tagging and entity recognition inside the same process as your application, without a Python service. Do not adopt it if you need transformer-grade accuracy, a large published accuracy table, or a model registry you do not control. Before committing, verify three things: which catalyst.models packages exist for your target language and their version numbers, whether Storage.Current pointing at a local directory is acceptable for your deployment, and whether the entity recognizers you need are covered by the gazetteer, PatternSpotter or AveragePerceptronEntityRecognizer, since the README does not claim a neural NER model.
Community notes