Repository navigation
Ollama removed LoRA-adapter GGUF *creation* (v0.34.1+) — breaks adapter provisioning for #1634, serving unaffected #1651
Description
Summary
Ollama removed the ability to create/build new LoRA-adapter GGUF models as of v0.34.1 (via ollama/ollama#14969, merged 2026-09-15). Serving/inference of already-built adapter models is unaffected — this is a provisioning-time regression, not a runtime one. It blocks test/scripts/build_ollama_uncertainty_adapter.sh and the uncertainty_ollama.py example, and closes the only documented route for a new adopter to acquire an Ollama adapter tag for use with #1634's OllamaModelBackend.add_adapter/resolve_adapter.
This surfaced while validating #1643 (nightly script reliability): bumping the pinned Ollama version there (needed to fix an unrelated BlueVela crash) exposed this.
TL;DR: the Modelfile ADAPTER <lora.gguf> directive, used with ollama create -f Modelfile to fuse a LoRA into a new model tag, is what no longer works — Ollama's CLI now rejects any Modelfile containing it before the request even reaches the server. That is the exact mechanism test/scripts/build_ollama_uncertainty_adapter.sh uses. A model that was already built with ADAPTER on an older Ollama (or pulled pre-built from a registry) still loads and serves fine — the --lora flag is still passed to llama-server at runtime, just with a deprecation warning. Only the build-time ADAPTER directive is dead; run-time serving of an adapter already baked into a model is not.
(This issue was independently verified against primary sources — Ollama's actual PR diff, commit history, and current mellea source — before finalizing; corrections from that pass are folded in below.)
Root cause (verified against Ollama's own source)
ollama/ollama#14969— "create: add server-side MLX imports and drop GGUF conversion" (created 2026-03-19, merged 2026-09-15, +10,041/−28,435 lines across 187 files) — deliberately removed LoRA-adapter creation support as part of a larger architectural change: adding server-side MLX/safetensors import support and moving GGUF conversion/quantization responsibility out to llama.cpp tooling. Per the PR description: "Limit GGUF create to wrapping existing GGUF inputs into Ollama manifests... so GGUF conversion and quantization stay in llama.cpp tooling."- This was a deliberate removal, not incidental collateral: the diff removes LoRA-adapter creation across five sites — a new
errAdaptersUnsupportederror variable, a CLI-side pre-flight rejection,api.CreateRequest.AdaptersmarkedDeprecated: LoRA adapters are no longer supported, the metadata-based rejection gate, and outright deletion ofconvert/convert_gemma2_adapter.goandconvert/convert_llama_adapter.go— plus every mention of theADAPTERModelfile directive scrubbed fromdocs/modelfile.mdx. - Note: the PR's
Fixes #17179/Fixes #17180lines refer to two crash/goroutine-leak bugs in the old conversion pipeline, but those issues were filed four months after this PR was opened — they're closed as a side effect of deleting the subsystem, not the motivation for it.
- This was a deliberate removal, not incidental collateral: the diff removes LoRA-adapter creation across five sites — a new
- The rejection is enforced by three independent gates, none behind a flag:
cmd/cmd.go:335-336— client-side: the CLI itself rejects a Modelfile containing anADAPTERline before contacting the server, with the bare message"LoRA adapters are no longer supported". This is the gate our build script actually hits.server/create.go:86-88— request-level: an API request withAdaptersset is rejected with HTTP 400.server/create.go:894-895— metadata-level (only reachable if the above two are bypassed): the server reads the GGUF's own internal metadata (general.type, viaKind()infs/gguf/metadata.go) and rejects any file self-identifying as"adapter", with"%w: %s is a LoRA adapter".
- Confirmed source-consistent across versions:
v0.34.0'sserver/create.gostill has the oldconvert.ConvertAdapterpath;v0.34.1onward has the rejection gates above at the same locations as currentmain. Empirically reproduced (build fails identically) on macOS/Apple Silicon and on a BlueVela Linux/CUDA node on0.34.1; the same build succeeds on0.32.2. - Latest release is
v0.34.2(2026-09-15) — nothing newer, and given the rationale, unlikely to be reverted. - "Just convert the adapter independently with llama.cpp's own tooling?" Already the case, and not the blocker.
build_ollama_uncertainty_adapter.shproduces the adapter GGUF via llama.cpp'sconvert_lora_to_gguf.pyfrom a pinned llama.cpp checkout — never via Ollama's own (now-deleted) converter — and verifies the output is a correctly-typed adapter GGUF before proceeding. The failure is the next step: attaching that already-correctly-produced GGUF to a servable Ollama model tag viaollama create -f Modelfile(script lines 116-118). Ollama's rejection is keyed on the GGUF's own internal metadata (general.type == "adapter"), not on which tool produced the file, so no alternate converter changes the outcome — the block is on the consuming side (ollama create), not the producing side.
Scope: creation is dead, serving survives (on a deprecation clock)
Runtime/inference retains full adapter support, unchanged in v0.34.1, v0.34.2, and current main — only warned about, not removed:
server/images.gostill reads an adapter layer from an existing manifest intoAdapterPaths.server/sched.gostill passesAdapterPathsinto the runner launch and uses it in runner-reuse diffing.llm/llama_server.gostill passes each adapter tollama-serveras--lora <path>, now alongside aslog.Warn("LoRA adapters are deprecated and will be removed in a future release", ...).server/model.gostates the scope directly, in Ollama's own words: "LoRA adapters are deprecated; the adapter layer is carried over but new adapters cannot be created."PullModelhas no adapter gate either, so pulling a pre-built adapter tag from a registry also still works.
Two populations, different impact:
- A real mellea user with an already-provisioned adapter tag (built on
≤0.32.2, or pulled from a registry): unaffected. Upgrading Ollama blocks building new tags; it does not break serving existing ones. - Mellea's own tooling/CI: broken every run, unconditionally.
build_ollama_uncertainty_adapter.shalways builds fresh (ollama create -f Modelfileat line 118), so it hits gate 1 above on every invocation from0.34.1onward.docs/examples/intrinsics/uncertainty_ollama.pyshells to that script undersubprocess.run(..., check=True)when the model isn't pre-provisioned, so it raises rather than degrading.
The sharper edge underneath that split: OllamaModelBackend's adapter_models parameter ships with no defaults (mellea/backends/ollama.py:248,277 — empty dict; the docstring's "mellea-test/uncertainty-alora:latest" is a placeholder, not a real default), and the only documented route in this repo to obtain such a tag is the now-broken build script. So: existing installs keep working, but a new adopter currently has no working path to acquire the artifact at all unless they already have one, or a pre-built Granite adapter tag happens to be published on Ollama's registry (not checked as part of this issue).
Two things not settled by source inspection alone, flagged rather than asserted: (a) whether a manifest built on 0.32.2 loads end-to-end on 0.34.x without issue, given unrelated manifest-format changes shipped in between (#17619, #17858); (b) whether any adapter-fused Granite tag is currently published on ollama.com, which would restore a pull-based provisioning route for new users.
Impact on mellea
mellea/backends/ollama.py'sadd_adapter/resolve_adapter(added in feat(backends): support Ollama adapter functions #1634) are not directly broken — no mellea Python code callsollama create; they only register an I/O contract and select a tag string fromadapter_models. The break is entirely in how a tag gets created in the first place, not in mellea's runtime code.test/scripts/build_ollama_uncertainty_adapter.shfails outright on0.34.1+ (every run, no workaround short of using an older Ollama).docs/examples/intrinsics/uncertainty_ollama.pyfails when it falls back to self-building the adapter.- fix(test): reliable nightly GPU runs — phased execution as default, script-owned timings #1643 makes this failure non-fatal in the nightly script (a warning + the two affected tests fail/skip, instead of aborting the whole Ollama test phase) — that contains the blast radius for CI but doesn't restore provisioning.
Upstream context
Ollama has open work in this area, suggesting the removal may be a transitional step rather than a dead end: ollama/ollama#14032 ("llm: support multiple LoRA adapters and hot-swapping"), ollama/ollama#17377 ("server: support safetensors base models and float lora_alpha for LoRA adapters"), and issues #10358 ("Allow granite lora support") and #9548 ("Support hot-swapping for LoRA adapters") are all open.
Suggested next steps
- Decide whether an Ollama-served adapter needs a working provisioning path today, or whether it's acceptable to document "bring your own pre-built tag" until Ollama's own LoRA work (above) lands something new.
- If a provisioning path is needed now: the only route left is merging the LoRA into the base model's weights before GGUF conversion (e.g. via PEFT's
merge_and_unload()), producing one full model per adapter instead of a shared base + swappable adapter — a real storage/design tradeoff, not a config fix. - Track Ollama's upstream LoRA work (above) for a possible restored or improved provisioning path.
- Confirm that a served aLoRA actually activates, whatever provisioning route we end up with. On the local Hugging Face path an aLoRA can be loaded and reported as active while never activating, because the activation sequence its config declares never appears in the assembled prompt, and PEFT reports nothing when that happens (bug(adapters): detect when an aLoRA cannot activate, and stop selecting it silently #1678 for the Mellea-side detection, bug(adapters): published
requirement-checkaLoRA never activates, and scores worse than the LoRA #1679 for a concrete artefact mismatch in the publishedrequirement-checkaLoRA). Two things follow for this issue: the bundled tag our build script targets is an aLoRA, so the same question applies to it; and merging viamerge_and_unload()in step 2 above is not semantically equivalent for an aLoRA, since the weights are trained to apply only from an activation point onwards and merging applies them at every position. The ten-probe comparison harness from the local path transfers directly to an Ollama-served tag.
cc
@gabe-l-hart — flagging given your role on Ollama/llama.cpp Granite integration; you may already have visibility into ollama/ollama#14969 or the open LoRA PRs above, or want to weigh in on the right path forward.
References
- Removal PR: create: add server-side MLX imports and drop GGUF conversion ollama/ollama#14969
- Motivating bugs (closed as a side effect, not the cause): Bug 3: Unrecovered background goroutines let a crash in any of 20+ model converters take down the whole ollama serve process ollama/ollama#17179, Bug 4: Goroutine Leak on Partial GGUF Header Parse Failure in Lazy Reader ollama/ollama#17180
- Open upstream LoRA work: llm: support multiple LoRA adapters and hot-swapping ollama/ollama#14032, server: support safetensors base models and float lora_alpha for LoRA adapters ollama/ollama#17377, Allow granite lora support ollama/ollama#10358, Support hot-swapping for LoRA adapters ollama/ollama#9548
- Ollama releases: https://github.com/ollama/ollama/releases (latest
v0.34.2) - Related PR here: fix(test): reliable nightly GPU runs — phased execution as default, script-owned timings #1643
Activity
[-]Ollama removed LoRA-adapter GGUF support (v0.34.1+) — breaks #1634's Ollama adapter-serving design[/-][+]Ollama removed LoRA-adapter GGUF *creation* (v0.34.1+) — breaks adapter provisioning for #1634, serving unaffected[/+]gabe-l-hart commentedon Sep 18, 2026
https://github.com/ollama/ollama/blob/4f6f73983036588ec22059a27c9bbb00b3ce332f/server/model.go#L67 Yikes, this is indeed a problem. Seems like a pretty big deprecation, likely due to the complexity they saw with trying to implement parity on their MLX backend. There may be some hackery we could play by using the
LLAMA_ARG_MODELS_PRESETto work around Ollama and tell the backendllama-serverto find raw adapter GGUFs, but that's a pretty brutal hack.gabe-l-hart commentedon Sep 18, 2026
Confirmed the logical path for any adapters is now officially deprecated. Working backwards:
llama-serverdispatched with--loraarg: https://github.com/ollama/ollama/blob/4f6f73983036588ec22059a27c9bbb00b3ce332f/llm/llama_server.go#L387adaptersset frommodel.AdapterPaths: https://github.com/ollama/ollama/blob/4f6f73983036588ec22059a27c9bbb00b3ce332f/server/sched.go#L582C87-L582C99gabe-l-hart commentedon Sep 18, 2026
I think this makes it pretty clear: we should focus our efforts on https://llama.app/ for compositional adapters going forward.
filtercodes commentedon Sep 23, 2026
Confirming @planetf1's analysis. PR #17377 targeted the internal Go conversion logic (convert/*_adapter.go) to enable safetensors adapter ingestion. With #14969 deleting that conversion subsystem, #17377 cannot be merged in its current form.
@gabe-l-hart's llama.cpp does not have an MLX backend. For Mac users training LoRA models with MLX, since #14969 explicitly shifted Ollama toward server-side MLX/safetensors handling, there may still be a native safetensors/MLX adapter path in Ollama's roadmap (e.g., related to #14032), even if GGUF adapter creation is permanently dead.
planetf1 commentedon Sep 25, 2026
One thing worth folding into the provisioning decision, since the adapter this repo's build script targets is an aLoRA rather than a plain LoRA.
Merging is not equivalent for an aLoRA. Suggested next step 2 is to merge the LoRA into the base weights before GGUF conversion, via PEFT's
merge_and_unload(), producing one full model per adapter. For a plain LoRA that is the storage-versus-flexibility trade described above. For an aLoRA it also changes the semantics: the weights are trained to apply only from an activation point in the prompt onwards, with the base model's KV cache reused for the prefix. Merging applies them at every position, including the prefix, which is not the arrangement they were trained in. So a mergeduncertainty-alorais a different model from the adapter served with activation, and the output would need checking rather than assuming.Related, and cheap to fold in while this path is being reworked. On the local Hugging Face path we have just found that an aLoRA can be loaded and reported as active while never activating, because the activation sequence its config declares never appears in the assembled prompt. PEFT reports nothing when that happens. See #1678 for the Mellea-side detection, and #1679 for a concrete artefact mismatch in the published
requirement-checkaLoRA. Whatever provisioning route Ollama ends up with, the same question applies to a served adapter: does activation actually fire, and how would we know? Worth answering as part of this rework rather than as a separate exercise afterwards.The ten-probe comparison harness used on the local path transfers directly to an Ollama-served tag, so the same check can run once a provisioning route exists again.
gabe-l-hart commentedon Sep 25, 2026
Definitely a good point. Wouldn't creating a Granite Switch out of the adapters and the base model be the right path forward? This would avoid the deep-merge of the weights (thus breaking non-adapted use cases) while still packing a single GGUF. For Granite-based models, this should be well supported in Ollama passing through to
llama-serversince ggml-org/llama.cpp#25107.planetf1 commentedon Sep 29, 2026
A concrete addition to item 4 (confirm the served aLoRA actually activates), from the #1685 work.
Ollama's intrinsic path prefers the aLoRA
io.yamlwhenever the catalogue lists one (use_alora = AdapterType.ALORA in metadata.adapter_types,mellea/backends/ollama.py:456-457). So a served requirement-check aLoRA gets the same as-published instruction, starting<requirements>:. The Granite tokenizer merges>:into one token, so the declared invocation sequence ([27, 71226, 29],<requirements>) never appears in the prompt, and the adapter cannot activate whichever provisioning route we end up with.#1685 repairs that instruction on
LocalHFBackendonly. On Ollama the fix is the upstream republish tracked in #1679, followed by a catalogue pin bump. The uncertainty aLoRA, which is what the bundled-tag script builds, is not affected: its instruction contains its declared<certainty>sequence.Not yet checked: whether Ollama's server applies aLoRA invocation tokens at all. That decides whether this matters in practice there.
planetf1 commentedon Oct 20, 2026
Folded into #1633, whose evidence section quotes the key points from this thread (the llama.cpp redirect recommendation, the aLoRA-merge caveat, the Granite Switch packing suggestion, the activation question). The full discussion stays readable here. Closing so the Ollama decision has one entry point.