llm-lid
Token-level language identification with any LLM. Bring your own provider, model and API key, pass a text or a word, get one language label per token.
Install
pip install "llm-lid[anthropic]" # or [openai], [gemini], [all]
Quick start
from llm_lid import LanguageTagger
tagger = LanguageTagger(provider="anthropic", model="claude-sonnet-5-5", api_key="sk-ant-...")
result = tagger.tag("I no fit come today because rain dey fall for Lagos.")
result.pairs()
# [('I', 'eng'), ('no', 'pcm'), ('fit', 'pcm'), ('come', 'eng'), ..., ('Lagos', 'ne'), ('.', 'other')]
tagger.tag_word("abeg").label # 'pcm'
tagger.tag(["abeg", "come", "o"]) # pre-tokenized input, labels align with your tokens
tagger.tag_batch(list_of_texts, max_workers=8)
If api_key is omitted, the provider SDK reads its usual environment variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY).
Providers
LanguageTagger("openai", model="<model>", api_key="...")
LanguageTagger("gemini", model="<model>", api_key="...")
# Any OpenAI-compatible endpoint: OpenRouter, Groq, Together, DeepSeek, vLLM, Ollama
LanguageTagger("openai_compatible", model="<model>", api_key="...", base_url="https://openrouter.ai/api/v1")
LanguageTagger("openai_compatible", model="llama3.1", api_key="ollama", base_url="http://localhost:11434/v1")
# Your own function
LanguageTagger(lambda system, user: my_client.generate(system, user), model="my-model")
Provider options pass straight through: temperature (default 0 for OpenAI and Gemini; not sent to Anthropic unless you set it), max_tokens (Anthropic), json_mode (OpenAI and Gemini, default on), request_options (extra fields for the underlying request, e.g. {"output_config": {"effort": "low"}} for Anthropic), and client settings such as timeout. If a model rejects temperature, the call is retried without it.
Labels
By default labels are ISO 639-3 codes (eng, pcm, yor, hau, ibo, ...) plus mixed, ne, other and unk.
For a fixed scheme, pass your own labels. Descriptions go into the prompt.
tagger = LanguageTagger(
"anthropic", model="claude-sonnet-5-5",
labels={
"en": "English",
"pcm": "Nigerian Pidgin",
"yo": "Yoruba",
"mixed": "Intra-word mixing",
"ne": "Named entity",
"other": "Punctuation, numbers, emoji, URLs, @mentions",
},
instructions="Words shared by English and Pidgin take the language of the surrounding clause.",
examples=[(["Wetin", "you", "dey", "do", "?"], ["pcm", "pcm", "pcm", "pcm", "other"])],
)
other is assigned by rule to punctuation, numbers, emoji, URLs and @mentions. Change it with LabelSet.closed(labels, universal="univ"), or turn it off with auto_universal=False.
Output
result.labels # ['eng', 'pcm', ...]
result.tokens # TokenLabel(token, label, start, end, source) with character offsets
result.switch_points() # indices where the language changes
result.cmi() # Code-Mixing Index (Das and Gamback, 2014)
result.spans() # runs of the same label
result.to_dict() # JSON-ready
result.to_conll() # token<TAB>label lines
result.raw_responses # raw model replies, for auditing
How it works
- The text is tokenized locally, keeping character offsets and tonal diacritics.
- Tokens are sent to the model with their indices. The model returns a label per index as JSON.
- The reply is checked: every index present once, every label in the allowed set. If not, the error is sent back and the model retries (
max_retries, default 2). After that it raisesLabelingError, or withstrict=Falsemarks those tokensNone. - Texts longer than
chunk_sizetokens (default 200) are split near sentence ends.
A word on its own has no context, so words shared between languages (e.g. English and Pidgin) will be ambiguous with tag_word. Use tag on the sentence where possible.
Development
git clone https://github.com/anuoluwatuyi/llm-lid
cd llm-lid
pip install -e ".[dev]" && pytest
Citation
If you use llm-lid in research, please cite it using the metadata in CITATION.cff, or the "Cite this repository" button on GitHub.
License
MIT
Metadata
Release files for llm-lid 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| llm_lid-0.1.0.tar.gz | 13.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| llm_lid-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.1 kB
Release files / llm_lid-0.1.0.tar.gz
| Download URL | llm_lid-0.1.0.tar.gz |
|---|---|
| Size | 13.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7a54b465fc9c0f644fb0c2fe662c57a4b9a49fd7893cdce5ef773ef9a64b45e5
|
|
BLAKE2b-256 checksum How to use checksums |
20db0382ef8c55e42b77e4419eefd74d78d650d5686891bb36834181655902ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / llm_lid-0.1.0-py3-none-any.whl
| Download URL | llm_lid-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ea2a6d50fa0dba9981d2768a6d7d98f3b6a67eecd219cca6bbb0a00480920ad2
|
|
BLAKE2b-256 checksum How to use checksums |
64138d7bbe57c439defeb92bae29fd2083bb1aa7f5c0e91f12b6d9a3eea04c27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log