Natural Language Processing (NLP)
A plugin whose selector directive is search_terms is selected for a prompt that refers to
one of its search terms. smarter.apps.llmtool.nlp.does_refer_to() decides, trying
three kinds of matching in turn, and stopping at the first that succeeds:
Exact: the prompt contains every word of the search term, in any order, ignoring case, punctuation and plurals.
Tell me about Gobstoppersrefers toGobstopper. This is always on.Fuzzy: the prompt contains the search term with a typo or two, depending on its length.
Who is Lawrance McDaniel?refers toLawrence McDaniel. Theenable_plugin_fuzzy_matchingwaffle switch,ENABLE_PLUGIN_FUZZY_MATCHING, controls it, and is on by default.Thesaurus: the prompt contains a synonym of the search term, or of each of its words, from the WordNet thesaurus.
I want to buy an automobilerefers tocar, anda monochrome photographrefers toblack and white. Theenable_plugin_thesaurus_matchingwaffle switch,ENABLE_PLUGIN_THESAURUS_MATCHING, controls it, and is on by default.
Plugin selectors try exact and fuzzy matching on the prompt and on every user message first, and thesaurus matching last, on the prompt only.
Thesaurus matching
Thesaurus matching selects plugins more often than the others, since a word has many synonyms. To turn it off, if it selects plugins for prompts that do not need them, use Django admin, under Waffle > Switches, or:
python manage.py waffle_switch enable_plugin_thesaurus_matching off
To keep plugin selection precise, synonyms come only from the two most common senses of each
word, and only from its noun senses if it is a noun, since search terms are almost always
nouns. For example, the verb image means see, so Let me see the code does not refer
to image. Words of fewer than three letters, mostly pronouns and abbreviations, must match
exactly. WordNet does not know brand names, product names or most abbreviations, such as
admin, so search terms like these only match exactly, or with a typo.
WordNet is read with NLTK, whose corpus data the Docker image
installs into /home/smarter_user/nltk_data. Elsewhere, install it with
python -m nltk.downloader wordnet. Without it, thesaurus matching is disabled, with a
warning in the log.
Observability
Each successful match sends the
search_term_matched signal, with the prompt, the search
term, the kind of matching that succeeded (exact, fuzzy or thesaurus), and, for a
thesaurus match, the synonym that matched. The plugin app’s receiver logs it, so that you can
see why each plugin was selected.
Technical Reference
Natural language processing functions for OpenAI API-compatible providers.
- class smarter.apps.llmtool.nlp.MatchMethod[source]
Bases:
objectHow
does_refer_to()matched a search term.Sent as the
methodofsearch_term_matched.- EXACT = 'exact'
Every word of the search term is in the prompt.
See
simple_search().
- FUZZY = 'fuzzy'
The search term is in the prompt, with a typo or two.
- THESAURUS = 'thesaurus'
The search term, or a synonym of each of its words, is in the prompt.
See
thesaurus_match().
- smarter.apps.llmtool.nlp.PLURAL_SUFFIXES = ('s', 'es')
Suffixes with which a prompt’s word still matches a search term’s word, e.g. Gobstoppers and Gobstopper.
('s', 'es')
- smarter.apps.llmtool.nlp.THESAURUS_MAX_SENSES = 2
The number of a word’s most common senses from which its synonyms come.
A word has many senses in WordNet, ordered from the most to the least common. For example, the fifth noun sense of
caris an elevator car. Synonyms of rare senses would select plugins for prompts that have nothing to do with them. For the same reason, a word that is a noun only has the synonyms of its noun senses: the verbimagemeanssee, so thatlet me see the codewould otherwise refer toimage.
- smarter.apps.llmtool.nlp.THESAURUS_MIN_WORD_LENGTH = 3
Search term words shorter than this are matched exactly, never by synonym.
Short words are mostly pronouns and abbreviations, whose WordNet senses are rarely the intended ones. For example,
meis the abbreviation of Maine.
- smarter.apps.llmtool.nlp.allowed_distance(search_term)[source]
Return the number of typos to tolerate in a search term.
Longer search terms tolerate more typos, and short ones none, because a short word is only a few edits away from many unrelated words. For example,
APIis within three edits ofand,theandany.Search terms of fewer than 5 characters: 0.
Search terms of 5 to 8 characters: 1.
Longer search terms: 2.
Spaces are not counted.
- smarter.apps.llmtool.nlp.clean_prompt(prompt)[source]
Clean up a prompt by inserting spaces before capital letters.
This function transforms concatenated or camel-cased words into a more readable format by adding spaces before capital letters, except for names starting with “Mc”. Useful for improving prompt clarity in NLP tasks.
- Parameters:
prompt (
str) – The input string to clean.- Returns:
The cleaned string with spaces before capital letters.
- Return type:
Note
This is a simple heuristic and may not handle all edge cases perfectly. For example, names starting with “Mc” (e.g., “McDaniel”) are not split.
Tip
Use this function to preprocess user input or model prompts for better readability.
See also
Example usage:
from smarter.apps.llmtool.nlp import clean_prompt s = "WhoIsLawrenceMcDaniel" print(clean_prompt(s)) # Output: "Who Is Lawrence McDaniel"
- smarter.apps.llmtool.nlp.contains_phrase(prompt_words, phrase)[source]
Check if a list of words contains a phrase, as consecutive words.
Each word matches with
word_matches(), so plurals match, e.g.motor vehiclescontains the phrasemotor vehicle.- Parameters:
prompt_words (
list[str]) – The prompt’s words, as returned bynormalized_words().phrase (
str) – A lower case word or phrase.
- Returns:
True if the words contain the phrase, otherwise False.
- Return type:
- smarter.apps.llmtool.nlp.does_refer_to(prompt, search_term, threshold=None, fuzzy=None, use_thesaurus=None)[source]
Check if the prompt refers to the given string.
This function determines whether a prompt refers to a search term by first cleaning the prompt with
clean_prompt(), and then searching it withsimple_search(). If typo tolerant matching is enabled, it then also trieswithin_levenshtein_distance()and, if thesaurus matching is enabled,thesaurus_match(). When the prompt refers to the search term, thesearch_term_matchedsignal is sent, with theMatchMethodthat matched.- Parameters:
prompt (
str) – The input string to analyze.search_term (
str) – The target string to check for reference.threshold (
int|None) – An optional maximum Levenshtein distance for typo tolerant matching. Seewithin_levenshtein_distance().fuzzy (
bool|None) – Whether to tolerate typos. Defaults to theenable_plugin_fuzzy_matchingwaffle switch, which is on by default.use_thesaurus (
bool|None) – Whether to match synonyms, from the WordNet thesaurus. Defaults to theenable_plugin_thesaurus_matchingwaffle switch, which is on by default.
- Returns:
True if the prompt refers to the search term, otherwise False.
- Return type:
Important
Plugin selectors use this function to decide whether to select a plugin, which adds the plugin’s system prompt and tools to the conversation. Typo tolerant matching selects plugins more often, and it is controlled by
SmarterWaffleSwitches.ENABLE_PLUGIN_FUZZY_MATCHING. Thesaurus matching selects plugins more often still, and it is controlled bySmarterWaffleSwitches.ENABLE_PLUGIN_THESAURUS_MATCHING.Example usage:
from smarter.apps.llmtool.nlp import does_refer_to prompt = "WhoIsLawranceMcDaniel" print(does_refer_to(prompt, "Lawrence McDaniel", fuzzy=True)) # True print(does_refer_to(prompt, "Lawrence McDaniel", fuzzy=False)) # False print(does_refer_to(prompt, "John Doe", fuzzy=True)) # False print(does_refer_to("buy an automobile", "car", use_thesaurus=True)) # True
- smarter.apps.llmtool.nlp.lower_case_splitter(string_of_words)[source]
Split a string on spaces and return a list of lowercase words.
This function tokenizes a string by spaces and converts each token to lowercase. Useful for case-insensitive text processing, search, and normalization in NLP tasks.
- Parameters:
string_of_words (
str) – The input string to split and lowercase.- Returns:
List of lowercase words.
- Return type:
Tip
Use this function to prepare text for matching, searching, or comparison.
See also
Example usage:
from smarter.apps.llmtool.nlp import lower_case_splitter s = "The Quick Brown Fox" print(lower_case_splitter(s)) # Output: ['the', 'quick', 'brown', 'fox']
- smarter.apps.llmtool.nlp.normalized_words(text)[source]
Split a string into lowercase words, without punctuation.
- Parameters:
text (
str) – The input string.- Returns:
List of lowercase words, from which punctuation is removed.
- Return type:
Example usage:
from smarter.apps.llmtool.nlp import normalized_words print(normalized_words("Who is Lawrence P. McDaniel?")) # Output: ['who', 'is', 'lawrence', 'p', 'mcdaniel']
- smarter.apps.llmtool.nlp.simple_search(prompt, search_term)[source]
Check if the prompt contains every word of the search term.
This function performs a case-insensitive, whole word search. It returns True if each word of the search_term appears in the prompt, in any order, ignoring punctuation. A word also matches its plural, e.g.
Gobstoppersmatches the search termGobstopper, but it does not match a longer word that merely contains it, e.g.concatenatedoes not match the search termcat.- Parameters:
- Returns:
True if the search term is found in the prompt, otherwise False. An empty search term is never found.
- Return type:
Caution
This function does not tolerate typos. For typo tolerant matching, see
within_levenshtein_distance().Example usage:
from smarter.apps.llmtool.nlp import simple_search prompt = "Find all weather plugins for New York" print(simple_search(prompt, "weather plugins")) # True print(simple_search(prompt, "Weather")) # True print(simple_search(prompt, "plugin")) # True print(simple_search(prompt, "California")) # False print(simple_search(prompt, "the")) # False
- smarter.apps.llmtool.nlp.synonyms(phrase: str, nouns_only: bool = False) frozenset[str][source]
Return the synonyms of a word or phrase, from the most common senses of WordNet.
Synonyms come from the first
THESAURUS_MAX_SENSESnoun senses of the phrase, or, if it is not a noun, from the first senses of each of its other parts of speech. They are lower case, and multi-word synonyms have spaces, e.g.motor vehicle. The phrase itself is not included.- Parameters:
- Returns:
The synonyms, which are empty if WordNet is not installed, or does not know the phrase.
- Return type:
Example usage:
from smarter.apps.llmtool.nlp import synonyms print(sorted(synonyms("car"))) # Output: ['auto', 'automobile', 'machine', 'motorcar', 'railcar', 'railroad car', 'railway car'] print(sorted(synonyms("image"))) # Output: ['mental image', 'persona']
- smarter.apps.llmtool.nlp.thesaurus_match(prompt, search_term)[source]
Check if the prompt contains the search term, or synonyms of it, from the WordNet thesaurus.
The prompt matches if it contains a synonym of the whole search term, e.g.
monochromeforblack and white, or if, for each word of the search term, it contains the word, one of itssynonyms(), e.g.automobileforcar, or a noun of which it is a synonym. WordNet’s synonyms are not symmetric:imageis a synonym ofpicture, butpictureis not a synonym of the nounimage. Only nouns count in this direction, sinceweatheris a synonym of the verbendure. Words shorter thanTHESAURUS_MIN_WORD_LENGTHmust match exactly. Synonyms come only from each word’s most common senses, to keep plugin selection precise.- Parameters:
- Returns:
The words of the prompt that matched, e.g.
automobile, or None if the prompt does not match, or WordNet is not installed.- Return type:
See also
Example usage:
from smarter.apps.llmtool.nlp import thesaurus_match print(thesaurus_match("I want to buy an automobile", "car")) # 'automobile' print(thesaurus_match("show me a picture of a cat", "image")) # 'picture' print(thesaurus_match("what is the weather", "car")) # None
- smarter.apps.llmtool.nlp.within_levenshtein_distance(prompt, search_term, threshold=None)[source]
Check if the prompt contains the search term, allowing for typos.
This function compares the search term, case-insensitively and without punctuation, with each run of consecutive words in the prompt that has as many words as the search term. It returns True if any run is within the search term’s
allowed_distance()of it, measured as Levenshtein distance. For example, the search termLawrence McDanielmatchesLawrance McDaniel, andGobstoppermatchesGobbstopper.- Parameters:
prompt (
str) – The input string to search within.search_term (
str) – The target string to compare against.threshold (
int|None) – An optional maximum Levenshtein distance, which can only make the match stricter thanallowed_distance().
- Returns:
True if a run of words in the prompt is within the allowed distance of the search term, otherwise False. Search terms of fewer than 5 characters never match, because they tolerate no typos.
- Return type:
Example usage:
from smarter.apps.llmtool.nlp import within_levenshtein_distance prompt = "Who is Lawrance McDaniel?" print(within_levenshtein_distance(prompt, "Lawrence McDaniel")) # True print(within_levenshtein_distance("any ideas?", "API")) # False
- smarter.apps.llmtool.nlp.word_matches(prompt_word, term_word)[source]
Check if a prompt’s word matches a search term’s word.
The words match if they are equal, or if one is the plural of the other, formed with one of
PLURAL_SUFFIXES. Both words are expected to be normalized, as bynormalized_words().- Parameters:
- Returns:
True if the words match, otherwise False.
- Return type:
Example usage:
from smarter.apps.llmtool.nlp import word_matches print(word_matches("gobstoppers", "gobstopper")) # True print(word_matches("concatenate", "cat")) # False
- smarter.apps.llmtool.nlp.wordnet() LazyCorpusLoader | None[source]
Return the WordNet corpus reader, loading the corpus on first use.
WordNet is the thesaurus of
thesaurus_match(). It is read with NLTK, whose corpus data must be installed separately. If it is not installed, a warning is logged once, and thesaurus matching is disabled.- Return type:
LazyCorpusLoader|None- Returns:
The
nltk.corpus.wordnetreader, or None if the corpus is not installed.