Model or dataset
SeanLee97/AnglE avatar
SeanLee97/AnglE

AnglE: an angle-optimized loss and an inference wrapper for sentence embeddings

Train and Infer Powerful Sentence Embeddings with AnglE | 🔥 SOTA on STS and MTEB Leaderboard

573 stars37 forksPythonMIT

At a glance

What is it?
AnglE is a Python library that supplies a training objective for sentence embeddings and a single inference interface for BERT-based, LLM-based and BiLLM-based encoders. Its value is in the training loss and the model zoo it produced, not in the encode() call.
Who is it for?
Adopt AnglE if you are fine-tuning a BERT or LLM encoder on sentence-pair or NLI data and want the angle-optimized objective plus the CoSENT and Espresso losses in one package, or if you want to run the published UAE and angle-llama checkpoints through one code path. Do not adopt it if you only need vectors from an already-trained model; sentence-transformers and the raw HuggingFace transformers pipeline both cover that with fewer dependencies.
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 178 days ago.
What is it written in?
Mainly Python, 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 AnglE addresses is the training objective, not the encoder

Most sentence embedding libraries assume the hard part is already solved: you pick a checkpoint, call encode, and get vectors. AnglE starts one step earlier. The repository is built around a loss function described in the paper AnglE: Angle-optimized Text Embeddings (arXiv 2309.12871, accepted at ACL 2024 Main Conference), and the README frames the library as a way to train state-of-the-art BERT or LLM sentence embeddings with a few lines of code. The audience is therefore narrow and specific: people who have domain data (medical text, code, a private corpus) and need to fine-tune an encoder rather than rent one. The secondary audience is anyone who wants to run the checkpoints that came out of this work, since the same AnglE class handles inference for models trained elsewhere. If your only goal is to embed a corpus with a public model, the training half of this library is dead weight.

Four losses, three backbone families, two pooling conventions

The feature list is explicit about what you can train with. Four objectives ship: the AnglE loss itself, contrastive loss, CoSENT loss, and Espresso loss (described as ICLR 2025 work, also called 2DMSE, with details in a separate README_ESE file). Backbones split into BERT-based models (BERT, RoBERTa, ModernBERT and similar), LLM-based models (LLaMA, Mistral, Qwen), and bi-directional LLM-based models, the last of which the README points to an external repository, WhereIsAI/BiLLM, rather than describing in place. Training is listed as single-GPU and multi-GPU, with no mention of distributed launchers, schedulers or checkpoint resume behaviour in the material available. That gap matters: a loss library is easy, a training harness is where projects usually leave you to write your own loop. The pooling story is the part that will bite people in practice. BERT checkpoints in the official table use cls pooling with a 512 token limit. The LLM checkpoints use last-token pooling with a 4096 token limit and a prompt named Prompts.A. In the inference examples, pooling_strategy is passed explicitly at load time ('cls' for UAE-Large-V1, 'last' for the Llama-2 LoRA model). Mismatch that argument against the checkpoint and you get vectors that still look plausible but score worse, with no error raised.

How inference actually flows through the AnglE object

The mechanism visible in the README is a wrapper, not a new runtime. AnglE.from_pretrained loads a backbone, optionally attaches LoRA weights via pretrained_lora_path, sets a pooling strategy, and returns an object whose encode method turns text into vectors. For LLM models you must also pass is_llm=True; the README states this in bold as a requirement, which suggests the flag changes tokenization or pooling behaviour rather than just routing. torch_dtype=torch.float16 appears in the Llama-2 example, and .cuda() is chained on load, so the library assumes you are managing device placement yourself. Prompts are a first-class concept. Prompts.C is used for the query side in the retrieval example while documents are encoded without a prompt, and Prompts.A is used for the LLM similarity example. The README tells you to check what exists via Prompts.list_prompts(), and to use {text} as the placeholder inside a prompt string. Similarity is not part of the model output; the examples import cosine_similarity from angle_emb.utils and loop over document vectors manually. There is no index, no batching helper for large corpora, and no vector store in the material. AnglE produces vectors and stops.

Installation and the two inference paths

Installation is one command, offered two ways: uv pip install -U angle-emb or pip install -U angle-emb. The PyPI distribution name is angle-emb while the import name is angle_emb, which is worth noting because a search for one will not find the other. For BERT-based inference the README shows loading WhereIsAI/UAE-Large-V1 with pooling_strategy='cls', then calling angle.encode with a list of strings, to_numpy=True, and prompt=Prompts.C on the query side only. For LLM-based inference the load call grows to four arguments: the backbone name, pretrained_lora_path, pooling_strategy='last', is_llm=True, plus torch_dtype. The encode call then takes prompt=Prompts.A. Both paths end by importing cosine_similarity from angle_emb.utils and comparing vectors in a Python loop. Two Colab notebooks are linked, one per path, which is the fastest way to confirm the current API before you write your own code against it.

Where AnglE is the wrong tool

The clearest limitation is scope: AnglE is a training and encoding library, not a retrieval system. If you need chunking, an ANN index, metadata filtering or a served endpoint, none of that appears in the README, and you will be pairing it with something else. The second limitation is documentation depth relative to surface area. The README lists four losses and three backbone families but shows training code for none of them; it delegates detail to an external Read the Docs site and to a separate README_ESE file for Espresso. You cannot judge from the repository front page how a multi-GPU run is launched or how Espresso differs from the AnglE loss in implementation. The third is the prompt coupling. Retrieval quality depends on using the right prompt on the right side of the query/document split, and the README's own example applies Prompts.C to queries and nothing to documents. Getting that backwards is a silent failure, not an exception. Finally, the LLM path carries real operational weight: a 7B or 13B backbone in float16, loaded through LoRA, is a different resource class from a 512-token BERT model, and the 4096 token limit on the LLM checkpoints does not mean encoding a 4096-token document is cheap.

Sentence-transformers is the obvious alternative, and the difference is the loss

Sentence-transformers is the natural comparison because it also wraps transformer encoders, also exposes a load-and-encode interface, and also supports fine-tuning on sentence pairs. The difference in approach is the objective. Sentence-transformers ships its own losses (multiple negatives ranking, cosine similarity, and others) and its training loop; AnglE ships the angle-optimized loss from its paper alongside CoSENT and Espresso, and its published results are tied to that objective. The README states that mixedbread-ai/mxbai-embed-large-v1 reached an average MTEB score of 64.68 and WhereIsAI/UAE-Large-V1 reached 64.64, both trained using AnglE. Those numbers are the project's own claims about models trained with the library, not benchmarks of the library itself, and they say nothing about whether the loss will help on your data. The practical split: choose sentence-transformers if you want the larger ecosystem of pooling modules, evaluators and integrations; choose AnglE if the loss is the reason you are fine-tuning at all, or if you specifically want to run the UAE and angle-llama checkpoints.

Maintenance, licensing and what the release history implies

The repository is MIT licensed, which permits commercial use and modification, though the licence covers the code and not the weights you download from HuggingFace; those carry their own terms and you should check each model card, particularly for the Llama-2-derived checkpoints. The project is not archived and the last push is dated 2026-03-22, so it is actively touched. Release cadence is uneven: v0.5.2 in October 2024, v0.5.6 in January 2025, then v0.6.0 in October 2025. That is roughly three releases across a year, with the largest gap between v0.5.6 and v0.6.0. For a library whose API surface is a from_pretrained class and an encode method, slow releases are not automatically a problem, but it does mean you should pin a version rather than float on the latest, and read the changelog between minors before upgrading. The dependency weight is the real ongoing cost: the LLM path pulls in transformers, torch and PEFT-style LoRA loading, and that stack moves faster than AnglE does. Budget for the upgrade work coming from the ecosystem, not from this repository.

Editorial conclusion

Adopt AnglE if you are fine-tuning a BERT or LLM encoder on sentence-pair or NLI data and want the angle-optimized objective plus the CoSENT and Espresso losses in one package, or if you want to run the published UAE and angle-llama checkpoints through one code path. Do not adopt it if you only need vectors from an already-trained model; sentence-transformers and the raw HuggingFace transformers pipeline both cover that with fewer dependencies. Before committing, verify three things: that the pooling_strategy you pass matches the checkpoint card, that your prompts come from Prompts.list_prompts() rather than being guessed, and that you can reproduce the training config for the backbone class you intend to fine-tune, since the README's training coverage stops at single-GPU and multi-GPU.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. SeanLee97/AnglE on GitHub
Community notes

Community notes