Model or dataset
elixir-nx/bumblebee avatar
elixir-nx/bumblebee

Bumblebee: Running Hugging Face Transformers Models from Elixir

Pre-trained Neural Network models in Axon (+ 🤗 Models integration)

1,668 stars147 forksElixirApache-2.0

At a glance

What is it?
Bumblebee is an Elixir library that loads pre-trained Transformer models from the Hugging Face Hub and runs them through Axon and Nx. It is a good fit for Elixir teams that want inference in the BEAM without a Python sidecar, and a poor fit for anyone who needs a model architecture the library has not implemented.
Who is it for?
Adopt Bumblebee if your model's architecture already appears in the library's module list and you want text, vision or audio inference inside an Elixir or Phoenix application without a Python process. Do not adopt it if your checkpoint is a Flax or TensorFlow export, or if your architecture has no Bumblebee implementation, because no loader will save you there.
Can I use it commercially?
Yes. Apache-2.0 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 28 days ago.
What is it written in?
Mainly Elixir, 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 gap Bumblebee fills between Elixir services and the Hugging Face Hub

Elixir applications that need a language model usually reach for a Python service, an external API, or a port to a Python process. Bumblebee takes a different route. It is described as an Elixir counterpart of Transformers, and it loads pre-trained models from the Hugging Face Hub directly into an Axon model running on Nx. The README frames the target audience by example: single-file samples of running neural networks inside Phoenix and LiveView apps live in examples/phoenix, and the recommended starting point is Livebook, where Smart Cells perform a task with a few clicks before you copy the code out. That is a clear signal about who this is for. It is for Elixir developers who want inference in the same runtime as the rest of their application, and who are willing to accept the set of architectures the library has implemented. It is not a training framework and it is not a bridge that runs arbitrary Python checkpoints.

What actually happens when you call Bumblebee.load_model

A Transformers-format repository does not contain a model. It contains trained parameters plus a configuration file, and the README is explicit that the model implementation lives in library code, in both Transformers and Bumblebee. So loading is a two-part operation: the configuration is fetched and used to build a matching model, then the trained parameters are fetched and paired with that model. The files involved are named in the README. config.json is the blueprint for construction. pytorch_model.bin and model.safetensors hold the tensors and both are supported. flax_model.msgpack and tf_model.h5 are not supported. Tokenization is separate: tokenizer.json and tokenizer_config.json describe how text becomes tensors, preprocessor_config.json does the same for images and audio, and generation_config.json carries sampling strategy and constraints for text generation. The consequence of this design is that support is per architecture, not per repository. If the class name under "architectures" in config.json has no matching module in Bumblebee, the load fails no matter how popular the checkpoint is.

The serving abstraction, and why the fill-mask example is the whole story

The README's fill-mask example is short and worth reading closely. It calls Bumblebee.load_model({:hf, "google-bert/bert-base-uncased"}), then Bumblebee.load_tokenizer for the same repository, then Bumblebee.Text.fill_mask(model_info, tokenizer), and finally Nx.Serving.run(serving, "The capital of [MASK] is Paris."). The return value is a map with a predictions list, each entry carrying a score and a token, with "france" at the top. What matters is the middle step. The model and the tokenizer are loaded separately and then combined by a task module, which is what turns raw weights into something with a defined input and output shape. Nx.Serving is the deployment surface: the same serving struct can be run inline, or handed to the Nx serving infrastructure for batched, concurrent requests. The documentation points to a Tasks section in the sidebar for these high-level APIs, with the remaining modules listing supported architectures. That split (tasks above, architectures below) is the mental model the library wants you to hold.

Installation, the EXLA backend, and the XLA_TARGET variable

Installation is two dependencies and one config line. In mix.exs you add {:bumblebee, "~> 0.6.0"} and {:exla, ">= 0.0.0"}. The README calls EXLA optional but important, because it compiles models just-in-time and runs them on CPU or GPU. Then in config/config.exs you set config :nx, default_backend: EXLA.Backend. For notebooks and scripts the equivalent is a Mix.install/2 call passing both dependencies with config: [nx: [default_backend: EXLA.Backend]]. GPU use is not automatic: the README says you must set the XLA_TARGET environment variable accordingly and links to the XLA repository's usage section for the accepted values. Note the version drift in the README itself. The install snippet pins bumblebee to ~> 0.6.0 while the releases list shows v0.7.1 from July 2026 and v0.7.0 from May 2026. Treat the snippet as illustrative and pin to a version you have actually resolved.

The support boundary is the architecture list, not the Hub

The most important operational fact in the README is that in order to use any given model, it needs to have an implementation in Bumblebee. There is no fallback path. The README offers three ways to check before you commit. You can call Bumblebee.load_model({:hf, "model-repo"}) and see whether it succeeds. You can use the repository inspector tool linked from the README, which runs a number of checks against the repository. Or you can open config.json, copy the class name under "architectures", and search the Bumblebee codebase for that keyword, for example BertForMaskedLM. If there is a match, the model is supported. There is a second wrinkle for multi-model repositories. The README cites stabilityai/stable-diffusion-2 as a case where several models live in separate subdirectories, and the fix is the subdir option: Bumblebee.load_model({:hf, "model-repo", subdir: "..."}). Without it you are pointing the loader at the wrong part of the repository. A third constraint sits in the serialization formats: PyTorch exports load, Flax and TensorFlow exports do not, so a repository that only ships flax_model.msgpack is unusable here regardless of architecture support.

Where Bumblebee is the wrong tool

Bumblebee is an inference library built around importing existing checkpoints. If your requirement is to train or fine-tune a model, nothing in the supplied material suggests that workflow; the README is about loading, tokenizing and serving. If your model is a TensorFlow or Flax export, the README states plainly that those formats are not supported, and converting the checkpoint is work you would be doing outside Bumblebee. If your architecture is not implemented, the library cannot help, and the README's own diagnostic path (search the codebase for the architecture class name) is designed to tell you that early rather than at runtime. There is also a quieter cost. Because the model implementation lives in library code, adding a new architecture means writing it in Elixir against Axon and keeping it aligned with the upstream Transformers definition. That is a maintenance surface you inherit if you depend on a model the project has not yet covered. The README does not document a plugin mechanism for third-party architectures, so this is not a gap you can close from your own application code with a small adapter.

How this differs from calling Transformers over Python

The obvious alternative is the Python Transformers library, either as a separate service or invoked as a subprocess. The difference is not speed, which the supplied material does not benchmark. The difference is where the model definition lives and what that buys you. Transformers ships implementations for a very large set of architectures and receives them as the research community publishes; Bumblebee reimplements a subset in Elixir and imports the same weights. Choosing Bumblebee means accepting a narrower architecture list in exchange for a model that runs inside the BEAM, is composed with Nx.Serving, and can be deployed as part of a Phoenix application or a Livebook notebook without a Python runtime on the host. Choosing Transformers means accepting a second runtime and an IPC boundary in exchange for near-immediate access to new architectures. There is a concrete test for which side you are on: paste your repository's architecture class name into the Bumblebee codebase search. A hit means the trade is available to you. No hit means the decision has already been made.

Licence and the cost of tracking upstream

Bumblebee is Apache-2.0. That covers the library. It does not cover the weights you download. Model repositories on the Hugging Face Hub carry their own licences, and nothing in the supplied material suggests Bumblebee inspects or enforces them, so the licence of google-bert/bert-base-uncased or any other checkpoint is a separate question you have to answer per model. On maintenance: the releases list shows v0.7.1 in July 2026, v0.7.0 in May 2026, and v0.6.2 in June 2025, with the last push in August 2026. The README's install snippet still pins ~> 0.6.0, which is one minor version behind the current line, so expect to read the release notes rather than the README when you upgrade. The upgrade cost that matters most is not the library version. It is the coupling between a Bumblebee architecture module and the upstream Transformers definition it mirrors. When upstream changes a model's configuration keys or parameter naming, the Elixir side has to follow, and your pinned model revision is what protects you in the meantime. Pin the revision explicitly when loading, and re-run the load against that revision after any Bumblebee upgrade.

Editorial conclusion

Adopt Bumblebee if your model's architecture already appears in the library's module list and you want text, vision or audio inference inside an Elixir or Phoenix application without a Python process. Do not adopt it if your checkpoint is a Flax or TensorFlow export, or if your architecture has no Bumblebee implementation, because no loader will save you there. Before committing, run Bumblebee.load_model({:hf, "your-repo"}) against the exact repository and revision you plan to ship, and confirm the returned struct matches the task module you intend to call.

Official sources

  1. elixir-nx/bumblebee on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Community notes

Community notes