Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions garak/langproviders/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
import re
import unicodedata
import string
import logging
from garak.resources.api import nltk
from langdetect import detect, DetectorFactory, LangDetectException

_intialized_words = False

Expand Down Expand Up @@ -104,20 +102,21 @@ def contains_invisible_unicode(text: str) -> bool:

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

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

DetectorFactory.seed = 0
# Imported lazily so the langid model is only loaded when language
# detection is actually needed (e.g. reverse-translation judging).
import langid

# Detect Language: Skip if no valid language is detected
try:
lang = detect(text)
except LangDetectException:
logging.debug("langdetect failed to detect a valid language.")
# Length and pattern checks: Skip if it's too short or repetitive
if len(text) < 3 or re.match(r"(.)\1{3,}", text): # e.g., "aaaa" (4+ repeats)
return False

if lang == "en":
# Skip if there is no linguistic content to detect a language from
if not any(char.isalpha() for char in text):
return False

# Length and pattern checks: Skip if it's too short or repetitive
if len(text) < 3 or re.match(r"(.)\1{3,}", text): # e.g., "aaaa" or "123123"
# Detect Language: Skip if the text is English (no translation needed)
lang, _ = langid.classify(text)
if lang == "en":
return False

return True
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ dependencies = [
"nvidia-riva-client==2.16.0",
"google-cloud-translate>=2.0.4",
"grpcio-tools>=1.71.0",
"langdetect==1.0.9",
"langid==1.1.6",
"tiktoken>=0.7.0",
"mistralai==1.5.2",
"pillow>=10.4.0",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ ollama>=0.4.7
nvidia-riva-client==2.16.0
google-cloud-translate>=2.0.4
grpcio-tools>=1.71.0
langdetect==1.0.9
langid==1.1.6
tiktoken>=0.7.0
mistralai==1.5.2
pillow>=10.4.0
Expand Down
48 changes: 47 additions & 1 deletion tests/langservice/test_langprovision.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from garak.langservice import _load_langprovider
from garak.langproviders.base import split_input_text
from garak.langproviders.base import is_meaning_string, split_input_text


def test_split_input_text():
Expand All @@ -17,6 +17,52 @@ def test_split_input_text():
assert split_input_text(input_text) == expected_output


@pytest.mark.parametrize(
"text",
[
"Bonjour, ceci est une phrase en français.",
"これは日本語の文章です。",
"Hola mundo, esto es una frase en español.",
],
)
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



@pytest.mark.parametrize(
"text",
[
"This is a normal English sentence about cats.", # English needs no translation
"ab", # too short
"a", # too short
"", # empty
"aaaa", # repetitive
"123123123", # no alphabetic content
"12345", # no alphabetic content
"!!! ??? ...", # no alphabetic content
"$", # no alphabetic content
" ", # whitespace only
],
)
def test_is_meaning_string_false_for_meaningless_or_english(text):
assert is_meaning_string(text) is False


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)


Comment on lines +52 to +65

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

@pytest.mark.parametrize(
"langprovider_class, target_lang, model_name",
[
Expand Down
Loading