DEV Community

Cover image for I let an LLM judge inside Python's if statements, then ran Ansible's own tests on it
Yoshifumi Tamoto
Yoshifumi Tamoto

Posted on

I let an LLM judge inside Python's if statements, then ran Ansible's own tests on it

I wrote a small library that lets you put a question, in plain language, where a Python if condition goes.

from fuzzyif import fuzzy

if fuzzy("Is this message urgent?", msg):
    notify_oncall(msg)
Enter fullscreen mode Exit fullscreen mode

It is called fuzzyif. pip install fuzzyif. The code is here:

https://github.com/Tdual/fuzzyif

This post is about two things: what the library actually does, and whether it can replace a real pile of if/elif in a project everyone knows. I patched Ansible's distribution detection and ran Ansible's own test fixtures against the result.

The short version: the judgement part of the pile was replaceable. The extraction part was not. Finding exactly where that line falls was the most useful outcome.

Why would anyone want this

If you have ever routed support emails into "bug report", "how-to question" and "billing", you have written this:

if "error" in msg or "crash" in msg or "doesn't work" in msg:
    kind = "bug"
elif "how do I" in msg or "how to" in msg or "usage" in msg:
    kind = "howto"
elif "invoice" in msg or "charge" in msg or "refund" in msg:
    kind = "billing"
Enter fullscreen mode Exit fullscreen mode

This code loses the moment you write it. "The screen goes blank after login" is a bug report with none of the bug keywords. "What does this error mean?" is a how-to question that contains "error". Every keyword you add fixes one case and breaks another, and the ladder never stops growing.

What you wanted to write was the question itself: is this a bug report? fuzzyif lets you write that.

What is doing the judging

Behind fuzzyif is Jev, a model TypeSafe AI released in September 2026. They call it a "System One" model: it does not generate text. You give it a text and a question, and it returns a probability, a choice among labels, or a position on a scale. Values a program can use directly.

How fuzzyif works: if statement, fuzzyif, Jev, probability, threshold, True or False

fuzzy("Is this urgent?", msg) sends the question and the text to Jev, gets back a probability (say 0.93), and applies a 0.5 threshold. Because nothing is generated, a call on a warm connection takes about 0.25 s and produces about 20 output tokens. Identical question and text pairs are cached, and the HTTPS connection is reused.

Four kinds of question

  • fuzzy(question, text) returns a bool for a yes/no question.
  • fuzzy_match(text, {label: description}) picks one label. Use this when you want exactly one of several. Stacking fuzzy() calls in if / elif is not exclusive: if two questions both cross the threshold, the first branch wins even when the second was more likely.
  • fuzzy_batch(text, [q1, q2]) answers several yes/no questions in one request.
  • fuzzy_score(text, question, [level0, level1, ...]) returns a position on an ordered scale, for things like "how angry is the writer".

The support-email router becomes one call:

kind = fuzzy_match(msg, {
    "bug":     "a bug report",
    "howto":   "a how-to question",
    "billing": "a question about invoices or charges",
})
Enter fullscreen mode Exit fullscreen mode

Can it really delete a pile of if statements?

Toy examples prove nothing, so I set three conditions: a library everyone knows, an official test suite, and a target whose purpose anyone can understand.

I picked Ansible. The first thing Ansible does on a host is work out ansible_distribution (Ubuntu? RHEL?) and ansible_os_family (Debian-like? RedHat-like?). That decision lives in distribution.py, 786 lines, structured like this:

  • walk a list of release files: /etc/os-release, /etc/redhat-release, /etc/lsb-release, /etc/SuSE-release, and so on
  • match search strings in their contents ("Red Hat", "Amazon", ...)
  • dispatch to one of thirteen parse_distribution_file_* methods, each a ladder of if/elif over the file text
  • finally map the name to a family through OS_FAMILY_MAP, a hand-maintained table of about 70 entries

Before

Everything I deleted, in one picture. 418 lines, 84 if/elif. It is unreadable at this size on purpose: this is what a pile of if looks like.

Ansible distribution.py before the change: the 13 parser methods and OS_FAMILY_MAP

One of the thirteen at readable size. This is the SUSE parser, 67 lines.

The SUSE parser alone, 67 lines

After

I rewrote process_dist_files and deleted the thirteen parsers and OS_FAMILY_MAP. This is what is left.

After: process_dist_files builds evidence from the release files and asks fuzzy_match, 29 lines

It concatenates whatever release files exist into one block of evidence and asks fuzzy_match "which distribution is this". A second fuzzy_match asks "which family". The label sets are the distribution names Ansible already documents, with a one-line description each.

The file went from 786 lines to 450.

Ansible's own tests: 90 fixtures

Ansible ships 90 recorded fixtures: real /etc/*-release contents captured from machines, paired with the facts the collector must report. They cover 52 distributions. I ran that test unchanged against the patched code.

65 of 90 fixtures matched on every key. 25 differed on at least one key.

Per key, the picture is much sharper:

Per-key agreement across the 90 fixtures

  • distribution: 90 of 90
  • os_family: 87 of 88
  • version and major version: 88 of 90 and 84 of 84
  • distribution_release: 68 of 88, and this is where it fell apart

What the 25 differences were

I went through all 25. Almost none are wrong judgements. They are Ansible's house conventions for cutting substrings out of files:

  • SUSE puts only the service-pack number in release: VERSION="15-SP6" becomes 6
  • openSUSE Leap puts the minor digit there: 15.1 becomes 1
  • Clear Linux uses the literal string clear-linux-os
  • CentOS 8 reports Stream
  • only the Debian and Amazon parsers add a minor_version key
  • OSMC reads the string March 2022 from a custom file

None of these is a question of what something is. They are questions of which slice of the string to take. My patch left version and codename to the distro library that Ansible already uses as a baseline, so it does not reproduce those conventions.

The one real judgement miss: Ansible has two labels for the same UnionTech OS, Uos (Debian family) and UnionTech (RedHat family), chosen by which release files happen to exist. The judge picked the other one.

The lesson: you can delete judgement, you cannot delete extraction

Judgement suits fuzzy; extraction belongs to regex

Look closely at a pile of if and you find two different jobs mixed together.

One is judgement. "What distribution do these files describe?" "Is this database error a disconnect?" "Is this ticket a bug report?" These are about meaning. Written as keyword matches they grow without bound. They suit fuzzy.

The other is extraction. "Take the value of VERSION_ID." "Keep only the service-pack number." "Pull the codename out of the parentheses." These are about position, not meaning. A regex does them in one line, deterministically. There is no reason to hand them to a model.

fuzzyif replaces the judgement. Keep the extraction. Once you can see which lines are which, you know which part of the pile is safe to delete.

Where you should not use it

  • Anything you would not send to a third party. The text goes to an API. Passwords and personal data do not belong in a fuzzy() call. At one point I considered fuzzifying Django's password validators and dropped the idea for exactly this reason.
  • Extraction. See above.
  • Security decisions. A case near 0.5 can flip between runs. Do not put authorization behind a threshold.
  • Tight loops over large data. Every distinct text is a network call. Batch what you can and let the cache do the rest.

Implementation notes

  • Zero dependencies. The HTTP client is http.client from the standard library, one keep-alive connection per thread. First call about 0.6 s, later calls about 0.25 s.
  • LRU cache in front of every call. The same question and text never hit the API twice.
  • Retries with backoff on 429 and 5xx, honouring Retry-After.
  • mock() for tests: answer from a mapping without touching the API, so code that uses fuzzy stays unit-testable.
  • A bug I hit myself: http.client encodes str bodies as latin-1, so any non-Latin text failed. Bodies are now sent as UTF-8 bytes. Found while judging Japanese text; glad it made the first release.

Try it

pip install fuzzyif
Enter fullscreen mode Exit fullscreen mode

Put a TypeSafe API key in TYPESAFE_API_KEY or ~/.config/typesafe/api_key. The Ansible patch script and everything needed to reproduce the numbers above are in examples/ansible_distribution/ in the repository linked at the top.

Next I want to build the tool that reads an existing pile of if, separates judgement from extraction, and proposes the fuzzy rewrite for just the judgement part.

Top comments (0)