Skip to content

fix(moe/deepseek): create the output directory before writing config.json (#697) - #701

Open
Anai-Guo wants to merge 1 commit into
arcee-ai:mainfrom
Anai-Guo:fix/moe-deepseek-mkdir-outpath
Open

fix(moe/deepseek): create the output directory before writing config.json (#697)#701
Anai-Guo wants to merge 1 commit into
arcee-ai:mainfrom
Anai-Guo:fix/moe-deepseek-mkdir-outpath

Conversation

@Anai-Guo

@Anai-Guo Anai-Guo commented Aug 1, 2026

Copy link
Copy Markdown

Fixes #697.

Problem

DeepseekMoE.write_model() writes the output config with a plain open():

with open(os.path.join(out_path, "config.json"), "w", encoding="utf-8") as f:
    json.dump(out_cfg, f, indent=4)

Nothing creates out_path before that point:

  • mergekit.scripts.moe.build() calls out_arch.write_model(out_path, ...) directly — it never creates the directory itself.
  • initialize_io() (which constructs the TensorWriter that does create it) runs after the config.json write.

So a Deepseek MoE merge whose out_path does not already exist dies with FileNotFoundError before a single weight is written.

The other output architectures don't hit this because they persist their config through transformers:

writer config persistence
mergekit/moe/mixtral.py:118 out_cfg.save_pretrained(out_path)
mergekit/moe/qwen.py:113 out_cfg.save_pretrained(out_path)
mergekit/moe/qwen3.py:93 out_cfg.save_pretrained(out_path)
mergekit/moe/deepseek.py:121 raw open(...)

PretrainedConfig.save_pretrained() starts with os.makedirs(save_directory, exist_ok=True), so Mixtral/Qwen/Qwen3 get the directory for free. Deepseek builds a plain dict (it needs the auto_map remote-code keys), so it can't use save_pretrained and has to do this explicitly.

Fix

One line, immediately before the write:

os.makedirs(out_path, exist_ok=True)

exist_ok=True makes it a no-op for the already-common case where the directory exists, so behaviour is unchanged for every currently-working merge.

Notes

  • No test added: write_model() needs real source checkpoints to run end to end, and tests/ has no MoE coverage to hang a unit test off. The change is a strict superset of the previous behaviour.
  • Lint checked against the repo's pinned hooks — black 25.1.0 and isort 6.0.0 (with pyproject.toml settings and mergekit as first-party) both report the file clean.

🤖 Generated with Claude Code


Note

Low Risk
Single-line filesystem guard before config write; no change to merge logic or weight handling.

Overview
DeepSeek MoE merges no longer fail when the output path does not exist yet.

DeepseekMoE.write_model() writes config.json with a direct open() before initialize_io() runs, unlike Mixtral/Qwen paths that use save_pretrained() (which creates the directory). The change adds os.makedirs(out_path, exist_ok=True) immediately before that write so behavior matches the other MoE writers without affecting merges that already use an existing directory.

Reviewed by Cursor Bugbot for commit 8764ddd. Bugbot is set up for automated code reviews on this repo. Configure here.

…json

DeepseekMoE.write_model() opens out_path/config.json directly, while the
Mixtral/Qwen/Qwen3 writers persist their config through
PretrainedConfig.save_pretrained(), which does os.makedirs(..., exist_ok=True).
Nothing in mergekit.scripts.moe.build() creates out_path beforehand, so a
Deepseek MoE merge into a not-yet-existing output directory dies with
FileNotFoundError before any weights are written.

Create the directory first, matching what the other output architectures get
for free from save_pretrained().
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@Anai-Guo

Anai-Guo commented Aug 1, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

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.

Tiny bug in mergekit/moe/deepseek.py 1 line change.

1 participant