Skip to content

feat(studio): add DoRA support to studio#7315

Open
lzvxck wants to merge 10 commits into
unslothai:mainfrom
lzvxck:feature/dora-config
Open

feat(studio): add DoRA support to studio#7315
lzvxck wants to merge 10 commits into
unslothai:mainfrom
lzvxck:feature/dora-config

Conversation

@lzvxck

@lzvxck lzvxck commented Jul 21, 2026

Copy link
Copy Markdown

Add DoRA (Weight-Decomposed LoRA) as a training option in Studio

Motivation

Studio exposes LoRA, RS-LoRA, and LoftQ only. DoRA (use_dora=True) has been supported in Unsloth core and PEFT since 2026.04, fused kernels, with some training throughput overhead vs. plain LoRA (consistent with PEFT's documented DoRA cost), never surfaced in Studio.

Changes

  • Frontend: dora added as fourth LoraVariant. New pill, 2×2 grid. Wired through YAML/JSON serialization, model-defaults parsing, run-config override, i18n (12 locales).
  • Backend: use_dora added to TrainingRequest, threaded through routes/training.pytraining.pytrainer.py/worker.py into every get_peft_model() call site. NotImplementedError guard on MLX, mirroring the existing LoftQ guard.
  • Config assets: use_dora: false added to all 81 model-default YAMLs.
  • CLI: matching --use_dora flag in unsloth-cli.py and unsloth_cli/config.py.

No core DoRA implementation changes. Studio/CLI integration only.

Testing

  • End-to-end run on a consumer GPU: QLoRA + DoRA against Qwen/Qwen3-0.6B. Confirmed use_dora: true in the persisted job config
  • test_training_config_popover_source.py passes with use_dora added to the pinned key list.
  • All 81 YAMLs parse.

@lzvxck
lzvxck requested a review from danielhanchen as a code owner July 21, 2026 22:37
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69049e5c9a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

use_gradient_checkpointing = gradient_checkpointing,
random_state = config.get("random_seed", 3407),
use_rslora = config.get("use_rslora", False),
use_dora = config.get("use_dora", False),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward DoRA into fast-encoder embedding adapters

When the selected model is an embedding encoder that uses FastSentenceTransformer's fast-encoder path, this new flag is accepted here but unsloth/models/sentence_transformer.py builds LoraConfig directly in that path without forwarding kwargs or use_dora. Those runs will persist use_dora: true while training a plain LoRA adapter, so either plumb use_dora into that LoraConfig or hide/reject DoRA for embedding models until it is supported.

Useful? React with 👍 / 👎.

gradient_checkpointing: config.gradientCheckpointing,
use_rslora: config.loraVariant === "rslora",
use_loftq: config.loraVariant === "loftq",
use_dora: config.loraVariant === "dora",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate DoRA on adapter training

When a user selects DoRA for LoRA/QLoRA and then switches the method to Full Fine-tune, adapterMethod is false but this still serializes use_dora: true. On Mac/MLX the worker now rejects any use_dora before checking training_type/use_lora, so an otherwise full-finetune run fails with “DoRA is not supported for MLX training yet.” Please gate this on adapterMethod or clear the variant when leaving adapter training.

Useful? React with 👍 / 👎.

@lzvxck
lzvxck requested a review from Etherll as a code owner July 22, 2026 12:25
@danielhanchen

Copy link
Copy Markdown
Member

Thanks for this. I went through the whole change and ran a set of verification tests. It is clean, well scoped plumbing and is close to merge. Summary below.

What this does

Threads a use_dora boolean from the Studio frontend and the CLI down into every get_peft_model call site. No DoRA algorithm or kernel changes.

Correctness trace

I confirmed use_dora actually reaches peft.LoraConfig on every advertised path, so it is not a silent no-op:

  • Text: FastLanguageModel.get_peft_model forwards it through **kwargs into the LoraConfig(**arguments) call in unsloth/models/llama.py.
  • Vision: FastVisionModel.get_peft_model in unsloth/models/vision.py keeps it via the inspect.signature(LoraConfig) parameter filter.
  • Embedding: all three branches of FastSentenceTransformer.get_peft_model, including the fast encoder native LoraConfig.
  • CLI: unsloth-cli.py and unsloth_cli/config.py.

Both Codex points are addressed in c28b8cc (the fast encoder embedding branch now receives use_dora, and the frontend gates all variant flags on adapterMethod).

Verification

Static: all 81 changed YAMLs parse with a boolean use_dora: false default, the MLX guard raises as expected, and both CLI paths forward the flag. Frontend typecheck, production build and i18n parity pass with no new lint errors, and the new weightDecomposed key is present in all 11 locales.

Runtime activation tests on a single GPU, QLoRA where applicable, checking that the resulting adapter is genuinely DoRA (magnitude vectors present, trainable, receiving finite non zero gradients) and that a plain LoRA control has none:

Path Model peft_config.use_dora magnitude params grads finite + nonzero save/reload keeps magnitude notes
Text LoRA (control) Qwen3-0.6B 4bit false 0 n/a n/a (none) loss 1.98 to 1.52, 0.97 steps/s, 1.0 GB
Text DoRA Qwen3-0.6B 4bit true 196 yes, changes after optim step yes loss 2.91 to 1.82, 0.50 steps/s, 1.24 GB
Vision DoRA Qwen2-VL-2B 4bit true 324 yes not tested forward + backward ok, 3.24 GB
Embedding DoRA (fast encoder) all-MiniLM-L6-v2 true 37 yes not tested fast encoder path active, forward + backward ok

Losses and grad norms stay finite and trend down on both text runs. They are different parameterizations so exact parity is not expected, and the DoRA adapter save round trips with its magnitude tensors intact.

Optional follow ups (non blocking)

  1. On my run DoRA was about 1.9x slower than plain LoRA on Qwen3-0.6B (0.50 vs 0.97 steps/s) with slightly higher peak memory. That matches PEFT's documented DoRA overhead, so I would soften the "same speed as LoRA" wording in the description.
  2. The frontend correctly forces variant flags off for full finetuning and keeps them mutually exclusive, but a direct API, YAML or CLI caller can still submit full finetuning with use_dora: true, or more than one variant flag at once. On CUDA the full finetune path ignores it and MLX rejects it, so nothing breaks, but a small server side normalization (at most one variant, and none when adapters are off) would make it robust. The decode precedence also differs between run-config-override.ts and model-defaults.ts, which is harmless while the variants stay exclusive.
  3. Minor: the diffusion get_peft_model still drops use_dora. Out of scope here since Studio does not reach it, but worth a note either way.

Nice work. Happy to merge once you are good with the above.

@lzvxck

lzvxck commented Jul 23, 2026

Copy link
Copy Markdown
Author

Hi, thanks for your response, I noted all three follow ups and corrected them.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8e893c4d2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +742 to +744
value: "dora",
label: "DoRA",
desc: t("studio.params.weightDecomposed"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable DoRA on Apple Silicon

When Studio is running on Apple Silicon, this newly added DoRA option remains selectable because the existing Mac guard only clears/disables loftq, but _run_mlx_training now rejects any use_dora with “DoRA is not supported for MLX training yet.” As a result, Mac users can choose DoRA for an adapter run and get an immediate backend error instead of the option being coerced or disabled like LoftQ.

Useful? React with 👍 / 👎.

use_gradient_checkpointing = use_gradient_checkpointing,
random_state = 3407,
use_rslora = use_rslora,
use_dora = use_dora,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor DoRA when continuing existing adapters

When this path is used with a model that is already a PEFT adapter, FastLanguageModel.get_peft_model takes its pre-wrapped adapter branch and compares r, alpha/dropout, targets, use_rslora, etc., but not use_dora because DoRA is only arriving through kwargs. If a user continues training a plain LoRA adapter with DoRA selected, this call is treated as an identical adapter and skipped, so the run records use_dora: true while still training plain LoRA; add use_dora to the core signature/comparison or reject it for pre-wrapped adapters.

Useful? React with 👍 / 👎.

import type { BackendModelConfig } from "../api/models-api";

export type LoraVariant = "lora" | "rslora" | "loftq";
export type LoraVariant = "lora" | "rslora" | "loftq" | "dora";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate GGUF LoRA export for DoRA adapters

Once dora is a first-class adapter variant, users can train a DoRA adapter and then choose Studio's GGUF LoRA adapter export path, but export_lora_adapter() still calls save_pretrained_gguf(..., save_method="lora"), which goes through llama.cpp's convert_lora_to_gguf.py LoRA-A/B converter. DoRA adapters include magnitude-vector tensors in addition to lora_A/lora_B, so this export path fails or cannot represent the adapter; hide/reject GGUF LoRA adapter export for DoRA checkpoints unless they are merged and exported as a full GGUF model.

Useful? React with 👍 / 👎.

lzvxck added 2 commits July 23, 2026 10:30
…port, mismatch now correctly falls through to existing error instead of silently no-opping
Resolves the params-section.tsx conflict by keeping main's Mac-aware
disabled-state styling (flex-1, disabled:cursor-not-allowed/opacity-60)
and extending it to also cover DoRA, not just LoftQ, since DoRA is
likewise rejected by the MLX backend. Also extends the existing
stale-selection auto-clear effect (previously LoftQ-only) to reset to
"lora" if a persisted/imported config has loraVariant "dora" on Mac.
@lzvxck
lzvxck requested a review from Datta0 as a code owner July 23, 2026 13:33
@danielhanchen

Copy link
Copy Markdown
Member

Follow-up: I ran a deeper compatibility and cross-platform pass on this. Short version: the feature is additive and safe for everyone who does not turn DoRA on, but it needs a rebase before merge and there are a few small follow-ups.

Does it change existing behaviour?

No. use_dora defaults to false everywhere (config.get("use_dora", False), the pydantic field default, and all 81 YAMLs), and PEFT's own LoraConfig default is already false, so a LoRA / RS-LoRA / LoftQ / full run is byte-for-byte the same code path as before. The only core change is 4 lines in unsloth/models/sentence_transformer.py; llama.py and vision.py are untouched, so the text and vision LoRA paths are provably identical.

What I verified

  • Config compatibility (isolated venvs): all 81 YAMLs differ from base by use_dora: false only and still resolve false when the key is removed; TrainingStartRequest and the CLI Config default to false when the field is absent (old saved jobs load fine, no KeyError); pydantic extra="ignore" means an old backend ignores the new field rather than erroring.
  • Backend test suite on the merged tree: 8785 passed, 0 failed. Frontend typecheck, i18n parity (all 11 locales carry the new weightDecomposed key) and production build all pass.
  • No-regression parity on a B200: identical LoRA config on base vs this PR gives the same trainable-parameter set and matching loss curves (delta within run-to-run CUDA noise), with zero magnitude vectors in the LoRA control.
  • DoRA activation (text, vision, and the fast-encoder embedding path): the resulting adapter really is DoRA (magnitude vectors present, trainable, receiving finite non-zero gradients that move after a step). Works with 4-bit QLoRA and with non-zero dropout. Merging (save_pretrained_merged) folds it back into a normal 16-bit model that loads in plain transformers and generates correctly.
  • Cross-OS CI (Linux / Windows / macOS runners, CUDA-spoofed): the DoRA-relevant jobs are green, including Windows API/UI/GGUF, MLX on Mac M1, Frontend, Lint, Version Compat and Wheel. Two red jobs are unrelated to this PR: Backend CI fails only on features/settings/tabs/voice-tab.tsx (text-[9px]/[10px], a font-scale-contract check that already fails on main), and Mac Studio UI CI is a pre-existing chat-compare timeout.
  • Browsers: the new 2x2 pill grid renders and selects correctly in Chromium (Chrome/Edge), Firefox and WebKit (Safari), including narrow width and Arabic RTL.

Suggested follow-ups (none block the concept, but please rebase first)

  1. Rebase onto main. It currently conflicts with feat(studio): Mac-aware training controls for MLX (optimizers, LoftQ, packing) #7358 in params-section.tsx; resolve so both the Mac-aware LoftQ disable/typography and the new 2x2 grid survive (keep the disabled: styles, drop the old flex-1).
  2. The MLX guard for DoRA runs before use_lora is derived, so a full-finetune MLX job carrying a stale use_dora=true would wrongly raise. Gate it on adapter training (same as would apply to the existing LoftQ guard).
  3. Only the frontend enforces one-variant / off-under-full-finetune. A direct API/YAML/CLI caller can still send multiple variant flags or use_dora=true with full finetune. A small server-side normalisation would harden it.
  4. DoRA has real overhead vs plain LoRA (I measured about 1.9x slower on Qwen3-0.6B), matching PEFT's docs, so I would soften the "same speed as LoRA" wording.
  5. Minor: the legacy WizardState/WizardActions union in src/types/training.ts still lists only lora/rslora/loftq (it is unused, so no runtime effect); and DoRA on fused-MoE target_parameters is rejected by PEFT, so those models should reject DoRA early rather than fail late. Also the description says 81 model-default YAMLs / 12 locales; it is 78 defaults + 3 templates and 11 locales.

Nice feature. With the rebase and the MLX guard tweak it looks good to me.

@danielhanchen

Copy link
Copy Markdown
Member

Thanks for the fast turnaround. You already covered the main follow-ups from my last pass: the rebase/merge of main, the mutually-exclusive variant validation with a clear rejection under full finetuning, disabling DoRA on Mac, the GGUF LoRA-export preflight guard, and the diffusion note. That all looks good.

I pushed one tiny consistency fix on top: the legacy WizardState/WizardActions union in studio/frontend/src/types/training.ts still listed only lora | rslora | loftq, so I added dora to match the canonical LoraVariant. It is currently unused code, so no runtime effect; it just keeps the two unions in sync. Typecheck and build are green.

Good to merge from my side once CI is green on the rebased branch.

@lzvxck

lzvxck commented Jul 24, 2026

Copy link
Copy Markdown
Author

Thanks you, happy to help!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 520b18670d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread unsloth-cli.py
random_state = args.random_state,
use_rslora = args.use_rslora,
loftq_config = args.loftq_config,
use_dora = args.use_dora,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject DoRA in the legacy MLX CLI path

When unsloth-cli.py is invoked on Apple Silicon with --use_dora, this new argument is still forwarded into FastLanguageModel.get_peft_model; on MLX that public shim routes to FastMLXModel.get_peft_model, while the Studio MLX worker in this same change explicitly rejects DoRA as unsupported before training. This leaves the legacy CLI exposing a Mac-only adapter option that cannot run correctly, so guard it on is_mlx (or fail with the same clear error) before calling get_peft_model.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants