02 Feb 2026

feedPlanet Mozilla

Firefox Nightly: Profile, search and wallpaper bug fixes plus more! – These Weeks in Firefox: Issue 195

Highlights

Separate search bar with search results on the Firefox toolbar, now visually displaying a new x icon to clear input.

Friends of the Firefox team

Resolved bugs (excluding employees)

New contributors (🌟 = first patch)

Project Updates

Add-ons / Web Extensions

Addon Manager & about:addons
WebExtensions Framework
WebExtension APIs

AI Window

DevTools

// Maximum supported value of arguments.length. This bounds the

// maximum number of arguments that can be supplied to a spread call

// or Function.prototype.apply. This value also bounds the number of

// elements parsed in an array initializer. NB: keep this in sync

// with the copy in builtin/SelfHostingDefines.h.

static const unsigned ARGS_LENGTH_MAX = 500 * 1000;

WebDriver

Lint, Docs and Workflow

New Tab Page

Profile Management

Search and Navigation

UX Fundamentals

02 Feb 2026 6:04pm GMT

The Mozilla Blog: AI controls are coming to Firefox

tylized Firefox AI controls interface highlighting the option to block AI enhancements with dropdown controls for AI features.
AI controls showing the option to block AI enhancements.

AI is changing the web, and people want very different things from it. We've heard from many who want nothing to do with AI. We've also heard from others who want AI tools that are genuinely useful. Listening to our community, alongside our ongoing commitment to offer choice, led us to build AI controls.

Starting with Firefox 148, which rolls out on Feb. 24, you'll find a new AI controls section within the desktop browser settings. It provides a single place to block current and future generative AI features in Firefox. You can also review and manage individual AI features if you choose to use them. This lets you use Firefox without AI while we continue to build AI features for those who want them.

One place to manage your AI preferences

Firefox offers AI features to enhance everyday browsing. These features are optional, and they're easy to turn on or off.

At launch, AI controls let you manage these features individually:

You can choose to use some of these and not others. If you don't want to use AI features from Firefox at all, you can turn on the Block AI enhancements toggle. When it's toggled on, you won't see pop-ups or reminders to use existing or upcoming AI features.

Once you set your AI preferences in Firefox, they stay in place across updates. You can also change them whenever you want.

Stylized Firefox AI controls interface highlighting the option to block AI enhancements with dropdown controls for AI features.
Firefox AI controls overview.

The browser that gives you a say

AI controls give you more say in how you move across the web.

We believe choice is more important than ever as AI becomes a part of people's browsing experiences. What matters to us is giving people control, no matter how they feel about AI.

If you'd like to try AI controls early, they'll be available first in Firefox Nightly. We'd love to hear what you think on Mozilla Connect.

Take control of your internet
Download Firefox

The post AI controls are coming to Firefox appeared first on The Mozilla Blog.

02 Feb 2026 5:00pm GMT

31 Jan 2026

feedPlanet Mozilla

Tarek Ziadé: Catching Code Complexity with a Local LLM

Performance issues in Python often don't look like bugs.

They don't crash, they don't fail tests, and they don't stand out in code review. They just quietly turn into cliffs when the input size grows.

This post is about one such performance fix in transformers, what it revealed, and a small experiment that came out of it: LoopSleuth, a local LLM-powered complexity scanner.

It Started With a Tokenizer Converter

While working on transformers, I fixed a performance issue in convert_slow_tokenizer.py that took a tokenizer conversion step from 4 minutes down to ~1 second when running on very large vocabularies (100k+ tokens).

The Test That Surfaced It

This started when CI flagged test_voxtral_tokenizer_converts_from_tekken as the slowest test in the suite.

The test loads mistralai/Voxtral-Mini-3B-2507 and forces the fallback path to TokenizersBackend.

That fallback triggers the slow→fast tokenizer conversion step - and that conversion was doing repeated .index() lookups inside a sort key, turning large vocabularies into a performance cliff.

The root cause was a classic scaling trap.

The Original Pattern

# BEFORE (simplified excerpt)
for rank, token in enumerate(bpe_ranks):
    local = sorted(
        local,
        key=lambda x: (
            bpe_ranks.index(x[0]),
            bpe_ranks.index(x[1]),
        ),
    )

(Simplified excerpt - the key issue is the repeated .index() inside the sort key.)

At first glance this looks harmless.

But list.index() is O(n).

And the real killer is that it happens inside a sorted() key function.

Sorting local means computing the key for every element, and each key performs two linear searches through bpe_ranks: sorted() calls the key function once per element (O(m)), and each key calls .index() twice (O(n)), so the total becomes O(m·n) - often a scaling trap when m and n are both large.

The Fix

# AFTER (reduces key computation from O(n) to O(1))
token_to_rank = {token: rank for rank, token in enumerate(bpe_ranks)}

for rank, token in enumerate(bpe_ranks):
    local = sorted(
        local,
        key=lambda x: (
            token_to_rank[x[0]],
            token_to_rank[x[1]],
        ),
    )

The optimization is simple:

This doesn't eliminate all sorting work (the outer loop still sorts repeatedly), but it removes the quadratic lookup cost that was dominating runtime.

The takeaway wasn't just "use dicts" - it was that asymptotic traps often hide in perfectly valid Python idioms.

Could This Have Been Caught Automatically?

After landing that fix, I kept wondering:

How many other places in the codebase have the exact same pattern?

This wasn't a correctness issue:

And none of the linting tools I normally rely on flagged it.

Ruff's PERF rules catch obvious constructs like unnecessary list copies, but they don't reason about .index() inside a sort key.

In theory, a linter could detect patterns like:

But most rule-based linters avoid making strong claims about asymptotic complexity.

That's a reasonable trade-off: linters are fast, deterministic, and low-noise - but they often miss scaling issues unless you add very specific custom rules.

This is where I started wondering whether an LLM could help fill the gap.

Scanning Transformers With Claude

As an experiment, I ran Claude Code over the repository with one question:

Find quadratic complexity patterns similar to the tokenizer converter bug.

The result was surprisingly useful.

It scanned ~3,000 Python functions across the codebase in a few minutes and flagged ~20 instances of the same anti-pattern:

About half were genuine hot-path candidates; others were technically quadratic but not performance-critical in practice.

For example:

ymls.sort(key=lambda x: results.index(x[:-4]))

Or:

aspect_ratios_ids[i, j] = supported_aspect_ratios.index(ratio)

All fixable with the same technique:

lookup = {val: idx for idx, val in enumerate(reference)}

This report was a great proof of concept.

But it relied on a large hosted model.

The Question Became: Can This Work Locally?

Instead of running a massive model in the cloud, I wanted to know:

That's how I ended up hacking together a small prototype I called LoopSleuth.

Why Rust + llama.cpp?

My first instinct was to build this as a Python script on top of transformers itself.

But I wanted this experiment to be:

A single static binary makes it easy to drop into CI, like Ruff.

And honestly, I also wanted an excuse to explore the Rust ecosystem that powers tools like Ruff and Ty.

So LoopSleuth is written in Rust and uses:

In practice, a small model like Qwen2.5-Coder 3B (Q4) already gives surprisingly good results for this narrow task.

LoopSleuth: A Small Complexity Scanner

LoopSleuth is a CLI tool that:

  1. parses Python modules
  2. extracts functions (each function is analyzed in isolation: signature + body, without full module context)
  3. sends each function to a local LLM
  4. asks a focused question:

Does this contain patterns that may scale quadratically?

If the model answers "QUADRATIC", it also asks for an optimization suggestion.

This framing treats complexity as a heuristic warning (like a linter) rather than a mathematical proof.

How It Works

The prompt is deliberately simple and constrained:

Classify this function as OK or QUADRATIC.
Look for list.index(), nested loops, or linear operations inside loops.
Return only one word: OK or QUADRATIC.

This makes the model focus on structural patterns rather than trying to perform full dataflow analysis, and the constrained output format makes parsing reliable.

Example output:

⚠️  Functions with O(n²) complexity: 5

🔴 QUADRATIC COMPLEXITY DETECTED IN:
  • bubble_sort
  • find_duplicates
  • remove_elements
  • string_concatenation
  • nested_comparison

Because it's a CLI, it can be used in a few practical ways:

Why Not Just Use Existing Linters?

Before building anything, I tried the usual suspects.

Tools like Ruff, Pylint, and performance-focused plugins can catch a lot:

But none of the linters I tried really caught the pattern that triggered this whole experiment:

These tools are excellent at enforcing specific rules, but they generally don't try to answer:

"Does this function scale quadratically with input size?"

That gap is what made the LLM approach interesting to explore.

A Quick Comparison

One thing I wanted to sanity-check early was whether existing linters would catch the same issues.

So I built a small test file with a handful of intentionally quadratic functions (nested loops, .remove() in loops, string concatenation, etc.) and ran:

The results were pretty stark:

Tool Detects .index() in loop? Reports complexity?
Ruff
Pylint
LoopSleuth ✅ (heuristic)

LoopSleuth flagged all 5 quadratic functions, while Ruff and Pylint flagged plenty of style and quality issues but neither directly reported algorithmic complexity problems.

This isn't really a criticism of those tools - they're simply not designed for that job.

I wrote up the full comparison here:

To be clear, there may be ways to approximate some of these checks with custom rules or plugins, and linters remain the first line of defense for code quality.

LoopSleuth is just exploring a different axis: scaling behavior.

Still an Experiment

LoopSleuth is not a replacement for linters.

It's a small experiment.

Traditional linters like Ruff or Pylint excel at catching specific code smells. But most scaling bugs don't come from a single construct. They come from composition:

Rule-based linters struggle to infer:

LLMs, even small ones, can often reason about these patterns more directly.

That said, LoopSleuth runs against isolated Python functions one by one, which means it doesn't yet understand:

Limitations

Like any heuristic tool, LoopSleuth has trade-offs:

False positives:

False negatives:

The accuracy depends heavily on prompt design and model choice.

Important: LoopSleuth is a screening tool, not a replacement for profiling or benchmarking. It flags patterns that may cause issues, but only real measurements can confirm actual performance problems.

More broadly, I'm interested in whether this approach can extend beyond complexity analysis to other classes of performance issues.

One direction would be to build a small library of prompts for:

And in an ideal world, we could fine-tune a small model (like Qwen2.5-Coder 3B) to specialize on this kind of performance reasoning.

What's Next

If this experiment proves useful, here are some directions worth exploring:

Right now LoopSleuth is a proof of concept, but these extensions could make it practical for real codebases.

Conclusion

LoopSleuth started as a simple question:

Could we catch quadratic complexity bugs automatically?

The answer is: not perfectly.

But even a small local model can spot surprising amounts of hidden O(n²) behavior.

And as codebases grow - especially ones like transformers - performance traps scale with them.

LoopSleuth is a small experiment toward making complexity visible earlier.

If you're interested, the project is here:

If you have examples of hidden scaling bugs or want to contribute detection patterns, I'd love to collect them as test cases. Feel free to try it locally or open an issue.

31 Jan 2026 12:00am GMT