The story that jumps out of this LocalAI analysis isn’t about the Go backend — it’s about core/http/static/assets/htmx.js, which contributes three of the five highest activity-weighted risk scores in the entire codebase. All five top hotspots are in the ‘fire’ quadrant, meaning they are both structurally complex and were touched within the last 30 days, making them live regression risks rather than background cleanup items. Across 7,825 total functions, Hotspots flagged 1,089 as critical — roughly 1 in 7 — and the pattern counts tell a consistent story: complex branching, deep nesting, and multiple exit paths appear in four of the five top functions simultaneously. I would start the review conversation with t in purify.js, which carries an activity-weighted risk score of 42.33 and a cyclomatic complexity of 189 — the single highest CC value in the top five by a wide margin.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top 5 Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
t | core/http/static/assets/purify.js | 42.3 | 189 | 8 | 73 |
he | core/http/static/assets/htmx.js | 36.2 | 76 | 5 | 52 |
Nt | core/http/static/assets/purify.js | 32.9 | 25 | 5 | 14 |
l | core/http/static/assets/htmx.js | 32.4 | 30 | 3 | 9 |
nt | core/http/static/assets/htmx.js | 32.1 | 37 | 15 | 11 |
Large Repo Analysis
LocalAI is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Codemod / Tooling Files in Results
All five top hotspots — t and Nt in core/http/static/assets/purify.js, and he, l, and nt in core/http/static/assets/htmx.js — are minified, bundled third-party JavaScript libraries (DOMPurify 3.0.6 and htmx 1.9.12 respectively). Their high complexity scores are intrinsic to how these libraries are built and distributed, not a reflection of LocalAI’s own engineering decisions. To exclude them from future analysis runs and surface the Go application layer properly, add the following to .hotspotsrc.json: { "exclude": ["core/http/static/assets/"] }. The alpine.js file appearing in context_only is the same situation — Alpine.js is a third-party UI library — and the same exclude pattern covers it.
Triage at a glance
7,825 functions analyzed
Every function in this repo sits in either the ‘fire’ or ‘watch’ quadrant — there are no ‘debt’ or ‘ok’ functions. That’s not a sign of negligence; it reflects a codebase that is actively evolving across a wide surface. The practical implication is that the usual argument for deferring cleanup — “it hasn’t been touched in months” — doesn’t apply here. If a function is structurally complex, it is also actively changing.
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Deeply Nested×4Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.Exit Heavy×4Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.Cyclic Hub×4Cyclic Hub
Participates in a call cycle with other high-traffic functions, creating circular dependency risk.Hub Function×1Hub Function
Many other functions call this one — a change here ripples widely through callers.
Four patterns — complex branching, deeply nested control flow, multiple exit paths, and hub-like coupling — appear in four of the top five hotspots simultaneously. That co-occurrence is the key finding: these aren’t isolated problems but reinforcing ones. A function that is both deeply nested and exit-heavy has many paths to trace and many ways to leave, which means every test case has to account for a matrix of condition combinations. When that same function is also a cyclic hub — calling and being called by other complex functions — the blast radius of a silent regression grows accordingly.
t — purify.js
This is the top-ranked function in the entire analysis, with an activity-weighted risk score of 42.33. The source excerpt confirms it is the main IIFE factory for DOMPurify — essentially the entire library, including all its allowlists, sanitization logic, hook registration, and configuration management, bundled into a single minified function. A cyclomatic complexity of 189 means 189 independent execution paths through this code, each one a required test case for anyone modifying it. The nesting depth of 8 means that at the deepest decision points, a reviewer must hold eight layers of conditional context in their head simultaneously. With a fan-out of 73 distinct internal calls, the function acts as a hub: it initializes, dispatches, and coordinates nearly every other piece of DOMPurify behaviour.
The hub_function and cyclic_hub patterns are both present, which makes sense given the architecture — the outer factory function owns state that every inner function closes over. The exit_heavy pattern compounds the testing burden: the sanitization pipeline has many early-return branches for invalid input, unsupported environments, and in-place mode, each of which represents a path that must be verified independently.
The external signals show that all of the single commit touching this file in the window was classified as a bug fix. That’s a thin historical sample, but it’s consistent with the kind of one-off version bumps that bundled libraries receive.
Recommendation: This is a vendored third-party library (DOMPurify 3.0.6, as visible in the source). The structural complexity is an upstream concern, not something the LocalAI team should refactor inline. The right action is to add core/http/static/assets/purify.js to a .hotspotsrc.json exclude list so it stops dominating the risk table and lets genuine application-layer hotspots surface. If LocalAI ever needs to modify DOMPurify’s behaviour, a wrapper or configuration layer is far safer than patching the minified bundle.
he — htmx.js
This is the second-ranked function overall and the most complex in htmx.js, with an activity-weighted risk score of 36.19. Reading the source, he is htmx’s central AJAX request dispatcher — it handles target resolution, sync-mode logic (drop, abort, replace, queue), XHR lifecycle callbacks (onload, onerror, onabort, ontimeout), header assembly, form encoding, and Promise wrapping. The CC of 76 reflects the sheer number of conditional branches: sync policy checks, confirmation dialogs, prompt handling, URL parameter encoding, request queuing logic, and multiple early-return guards all run through this single function.
The nesting depth of 5 is moderate on its own, but combined with a fan-out of 52 — meaning this function directly calls 52 distinct other functions — the cognitive load on any reviewer is significant. The complex_branching and exit_heavy patterns are both present, and the exit-heavy characteristic is particularly visible in the XHR setup block, where a failed htmx:confirm event, an abort policy, or a failed URL validation each produces a separate early return.
That mirrors the purify.js pattern — the single commit in the window is also a version-bump commit classified as a fix.
Recommendation: Like purify.js, this is a vendored third-party file (htmx 1.9.12). Exclude it from Hotspots analysis via .hotspotsrc.json. If the team needs to customise htmx behaviour, the extension API (htmx.defineExtension) is the correct surface — not edits to the bundled source.
Nt — purify.js
With an activity-weighted risk score of 32.87, Nt is the attribute sanitization loop inside DOMPurify. Reading the source, it iterates over every attribute on a DOM element, applies the uponSanitizeAttribute hook, checks keep/force-keep flags, enforces self-closing attribute policy, strips template expressions when SAFE_FOR_TEMPLATES is set, validates the attribute name and value against the allowed-attribute map, handles namespaced attributes via setAttributeNS, and applies Trusted Types wrapping for TrustedHTML and TrustedScriptURL attribute types. Each of those decisions is a branch.
A CC of 25 is in the moderate-to-high range (the threshold for concern is typically CC 10; CC 25 means 25 independent paths). The nesting depth of 5, combined with complex_branching, deeply_nested, and exit_heavy patterns all being present, reflects the layered nature of the attribute policy: outer loop over attributes, inner checks for each attribute type, and conditional Trusted Types dispatch at the deepest level.
Recommendation: Same as for t — exclude the file. Nt is upstream DOMPurify code.
l — htmx.js
An activity-weighted risk score of 32.36 and a CC of 30. From the source, l is htmx’s makeFragment function — the HTML-to-DOM parser that wraps incoming server response strings into the correct document fragment type. It branches on whether the response is a full body, dispatches into template-fragment mode when useTemplateFragments is enabled (with a nested script-tag handling block), then falls through a large switch statement covering thead, tbody, tfoot, colgroup, caption, col, tr, td, th, script, style, and a default case. Each switch arm wraps the HTML string in a different table scaffolding to satisfy browser parsing rules.
The exit_heavy and cyclic_hub patterns are both present. The multiple return paths correspond to each case in the switch plus the early template-fragment return — every path returns a different kind of document fragment. The CC of 30 means a thorough test suite needs 30 cases just to cover this one parsing function.
Recommendation: Exclude the file. If LocalAI ever forks or patches htmx, the makeFragment function is a candidate for decomposition: each table-context case could be a named helper (makeTableFragment, makeRowFragment, etc.), which would reduce CC to single digits per function and make the intent of each wrapping strategy explicit.
nt — htmx.js
The third and final htmx.js hotspot, with an activity-weighted risk score of 32.11. The source reveals nt as htmx’s trigger-specification parser — it tokenizes the hx-trigger attribute value and builds an array of trigger spec objects, handling every-style poll triggers, SSE triggers, and the full set of modifiers: changed, once, consume, delay, from, target, throttle, queue, root, and threshold. Each modifier is parsed from a token stream in a nested loop, and the from modifier alone has sub-branching for closest, find, next, and previous selector variants.
The standout number here is the nesting depth of 15 — the deepest in the top five by a large margin, and well above the ND 8 threshold that typically signals a strong refactoring need.
A CC of 37 combined with ND 15 means this function has paths that are both numerous and deeply buried. The complex_branching and deeply_nested patterns are both present, and the combination is particularly hard to reason about: to understand what happens when a from:closest modifier appears inside an every trigger, a reader has to trace through multiple nested while/do-while loops and if-else chains before reaching the relevant code.
Recommendation: Exclude the file. If the team ever needs to extend htmx’s trigger syntax, the parser is the highest-priority refactoring target: a recursive-descent approach with named sub-parsers for each modifier type would dramatically reduce both CC and ND while making the grammar explicit.
The bigger picture
Three functions from htmx.js and two from purify.js consume the entire top-five list. The context_only entries — oe, ne, and Tr from htmx.js, and de and Wn from alpine.js — follow the same pattern: all are ‘watch’-quadrant functions in the same static asset files. Once those files are excluded, the structural risk picture of LocalAI’s actual Go codebase will become visible. The 1,089 critical-band functions are a real number, and some of them will be in Go code that the team owns and maintains. Right now, the static asset files are acting as noise that makes it harder to find those functions.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 4 |
deeply_nested | 4 |
exit_heavy | 4 |
cyclic_hub | 4 |
hub_function | 1 |
These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.
Reproduce This Analysis
git clone https://github.com/mudler/LocalAI
cd LocalAI
git checkout aae69b11632db2bcbd197a866e8890a100490111
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →