Skip to content

Migrate langdetect to langid.py with lazy loading (#1208)#1899

Open
nethum529 wants to merge 1 commit into
NVIDIA:mainfrom
nethum529:chore/issue-1208-langid
Open

Migrate langdetect to langid.py with lazy loading (#1208)#1899
nethum529 wants to merge 1 commit into
NVIDIA:mainfrom
nethum529:chore/issue-1208-langid

Conversation

@nethum529

Copy link
Copy Markdown

Migrates the language-detection dependency used by the language service from langdetect to langid.py, and loads it lazily. Closes #1208.

langid.py provides faster and more accurate detection across a wider range of languages than langdetect, and importing it inside is_meaning_string() means the model is only loaded when language detection is actually exercised (i.e. reverse-translation judging) rather than on every import of garak.langproviders.base.

What changed

  • garak/langproviders/base.py
    • Drop the module-level from langdetect import ... (and the now-unused import logging).
    • is_meaning_string() now uses a lazily-imported langid.classify().
    • Behaviour is preserved: langdetect previously raised LangDetectException on inputs with no linguistic features (e.g. pure digits/punctuation), which the old code treated as "not meaningful". langid never raises, so that path is replicated with an explicit "no alphabetic character → not meaningful" guard. The cheap length/repetition checks now run first so trivial inputs short-circuit before any model load.
  • pyproject.toml / requirements.txt: swap langdetect==1.0.9langid==1.1.6.
  • tests/langservice/test_langprovision.py: add unit tests for is_meaning_string (the function previously had none), covering non-English (→ translatable), English, too-short, repetitive, and no-feature inputs, plus a structural test asserting langdetect is gone and langid is imported lazily.

Verification

  • python -m pytest tests/langservice/test_langprovision.py -k "is_meaning_string or langid" → 14 passed
  • python -m pytest tests/test_reqs.py → passed (requirements.txt / pyproject.toml stay consistent)
  • Full suite collects cleanly with langdetect uninstalled (no other module imported it): python -m pytest --co → 4664 tests, exit 0
  • black --check garak/langproviders/base.py tests/langservice/test_langprovision.py → clean
  • Verify non-English text is still flagged meaningful and English / no-feature text is skipped
  • Verify langid is not imported at module load, only when is_meaning_string() runs

langid.py offers faster, more accurate detection across a wider range
of languages than langdetect. Import it lazily inside is_meaning_string()
so the model is only loaded when language detection is actually needed,
rather than on every import of garak.langproviders.base.

langdetect raised LangDetectException on inputs with no linguistic
features (treated as not-meaningful); langid never raises, so that path
is preserved with an explicit no-alphabetic-character guard, and the
cheap length/repetition checks now short-circuit before any model load.

Closes NVIDIA#1208

Signed-off-by: nethum529 <nethumweerasinghe.nw@gmail.com>
@leondz leondz self-requested a review July 6, 2026 16:51

@leondz leondz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple of minor changes, i like it in general

Comment on lines 103 to 104
def is_meaning_string(text: str) -> bool:
"""Check if the input text is a meaningless sequence or invalid for translation."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should rename function and change this comment - it's too cryptic

suggest "check the input text is suitable for translation"

and def is_suitable_for_translation

)
def test_is_meaning_string_true_for_non_english(text):
# Non-English, translatable text should be flagged as meaningful
assert is_meaning_string(text) is True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good, i like that we have coverage here

Comment on lines +52 to +65
def test_langproviders_base_uses_langid_lazily():
# Migration contract for issue #1208: langdetect is gone, and langid is
# imported lazily inside is_meaning_string rather than at module load.
import inspect

from garak.langproviders import base

module_source = inspect.getsource(base)
assert "langdetect" not in module_source
module_top = module_source.split("\ndef ")[0]
assert "import langid" not in module_top
assert "import langid" in inspect.getsource(base.is_meaning_string)


Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit too in the weeds and locks us in to a specific implementation pattern, i think it should be dropped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

migrate langdetect to langid.py; avoid needless loading

2 participants