gpt-oss を MLX-LM の API サーバで Dify や Open WebUI からムリヤリ使う方法

(Google のインデックス登録がうまくいかないので、タイトルとか内容とかちょこちょこいじってます)

先日アップした記事では、MLX バージョンの gpt-oss を MLX-LM の API サーバで動かすと Dify や Open WebUI 等では正しく動作しないと書きましたが、server.pyに変更を加えることでチャットだけはできるようになりました。手元の環境では動いていますが、ビビって MLX-LM には PR せずに Issue だけあげてあります (↓ 2コ)。本家が解決したら不要になる情報ですがせっかくなので共有します。

https://github.com/ml-explore/mlx-lm/issues/364 → (2025/08/15 加筆) 修正がコミットされたようなので、近くバージョンアップで修正されそうです

https://github.com/ml-explore/mlx-lm/issues/365 → (2025/08/15 加筆) 「クライアント側で対応すべき内容」と言うことでクローズされました

フォークした repo (↓) には変更済みのserver.pyを置いてあるので、よかったらどうぞ。Dify や Open WebUI 等の API クライアント側の対応を待たずに MLX-LM で gpt-oss が動きます。

https://github.com/tokyohandsome/mlx-lm

前回の記事はこれ:

各種バージョン等

Issue に書いてますが一応。

  • Open Web UI: v0.6.20
  • Dify: 1.7.1
% pip list|grep mlx
mlx 0.28.0
mlx-lm 0.26.3
mlx-metal 0.28.0
%
% python -V
Python 3.12.11

不具合の内容

詳細は上の issue を見てもらいたいのですが、ざっくり以下の内容です:

  1. LLM からのレスポンスが途中で終わってしまう: 何か制御コードみたいなものが含まれているのかと思ったら、ただのカラ文字が原因だった感じです。カラ文字は送らないようにしたら動くようになりました。トークナイザの不具合?
  2. チャットで 2つ目のプロンプトを投げるとサーバでエラーが発生する: 本来 API クライアント側で<|channel|>から<|message|>の思考部分をサーバに送り返さないのが正解だと思います (なので MLX-LM では上記 issue は対応無し)。ただまぁボクの場合、ローカルで動かしているだけなので、サーバで該当部分を捨ててしまうようにしました。

素人目にはそんなに根が深いわけではなさそうなので、かなり近いうちに修正されるんじゃないかと思ってます。(2025/08/15 追記) 1. は新しい MLX-LM のバージョンで修正されそうです。2. はクライアント側での対応が必要です。

手っ取り早く使うには

上記の通り現状 MLX-LM では gpt-oss 個別の対応はされないようで、Open WebUI や Dify などの API クライアント側での MLX 版 gpt-oss 対応を待たなければならないようです。ボクのように MLX の速さやにとりつかれていて API で MLX 版を使いたいという人は、server.pyだけ上書きして使ってみてください。mlxmlx-lmmlx-metalのバージョンは上記と合わせたほうが良いと思います。

仮想環境にクローンして使うならこんな感じです (ポートなどはご自由に)。

git clone https://github.com/tokyohandsome/mlx-lm
pip install -r requirements.txt
python -m mlx_lm.server --host 0.0.0.0 --port 9999 --log-level DEBUG

本家の MLX-LM を入れて、ボクがいじったserver.pyだけを差し替える方法も参考として貼っておきます。pipenvを使ってますがお使いの仮想環境でどうぞ:

mkdir gpt-oss_mlx-lm
cd gpt-oss_mlx-lm
pipenv --python 3.12 # 3.8 以上なら OK
pipenv shell
pip install mlx==0.28.0 mlx-lm==0.26.3

# インストールされたバージョンの確認
pip list|grep mlx

# 元のファイルを .original としてコピーしておく
mv .venv/lib/python3.12/site-packages/mlx_lm/server.py .venv/lib/python3.12/site-packages/mlx_lm/server.py.original
curl https://raw.githubusercontent.com/tokyohandsome/mlx-lm/refs/heads/main/mlx_lm/server.py -o .venv/lib/python3.12/site-packages/mlx_lm/server.py

上の最後の 2行で元のファイルをserver.py.originalとして保存し、変更済みのserver.pyをダウンロードしています。これで準備完了です。

以下コマンドで OpenAI API コンパチの MLX-LM サーバが起動します (例ではポート9999)。

mlx_lm.server --host 0.0.0.0 --port 9999 --log-level DEBUG

Open WebUI 等から gpt-oss に接続し、Terminal に流れるトークン全てが表示され、2回目以降もチャットが続けられれば成功です!

MLX-LM API Server のモデルを Open WebUI や Dify から使う方法は別記事に詳しく書いていますのでどうぞ:

ついでにserver.pyの変更箇所 (diff) を貼っておきます。+の行にあるのが今回追加した部分です:

diff .venv/lib/python3.12/site-packages/mlx_lm/server.py.original .venv/lib/python3.12/site-packages/mlx_lm/server.py
--- .venv/lib/python3.12/site-packages/mlx_lm/server.py.original	2025-08-15 21:05:24
+++ .venv/lib/python3.12/site-packages/mlx_lm/server.py	2025-08-15 21:15:34
@@ -694,2 +694,8 @@
             logging.debug(gen_response.text)
+
+            # --- Added from here ---
+            if not gen_response.text:
+                logging.debug("Skipping empty token.")
+                continue
+            # --- to here ---
 
@@ -837,3 +843,37 @@
             messages = body["messages"]
+
+            # --- Changes from here ---
+            # Modify message based on the `mlx-lm` chat template.
+            for message in messages:
+                if message["role"] == "assistant":
+                    content = message.get("content", "")
+                    if "<|channel|>analysis<|message|>" in content and "<|channel|>final<|message|>" in content:
+                        try:
+                            analysis_start_tag = "<|channel|>analysis<|message|>"
+                            analysis_end_tag = "<|end|>"
+                            final_start_tag = "<|channel|>final<|message|>"
+
+                            analysis_start = content.find(analysis_start_tag) + len(analysis_start_tag)
+                            analysis_end = content.find(analysis_end_tag)
+                            final_start = content.find(final_start_tag) + len(final_start_tag)
+
+                            analysis = content[analysis_start:analysis_end].strip()
+                            final = content[final_start:].strip()
+
+                            message["content"] = final
+                            message["thinking"] = analysis
+                        except Exception as e:
+                            logging.error(f"Failed to parse assistant message with analysis/final tags: {e}")
+                            # If parsing fails, leave the content and empty thinking
+                            message["thinking"] = ""
+            # --- to here ---
+
             process_message_content(messages)
+
+            # Moved response_format before `apply_chat_template`
+            if body.get("response_format", {}).get("type") == "json_object":
+                if self.tokenizer.chat_template is None:
+                    raise ValueError("JSON response format requested, but tokenizer has no chat template. Consider using `--use-default-chat-template`")
+                messages.append({"role": "user", "content": self.tokenizer.json_schema_prompt})
+
             prompt = self.tokenizer.apply_chat_template(

それでもまだ LM Studio が優れているところ

というわけでムリヤリながら Dify や Open WebUI でも MLX 版 gpt-oss でチャットができるようになったわけですが、OpenAI 社が推奨する思考部分をユーザから隠すということができません。そこは正式対応済みの LM Studio が勝っていますね。Dify や Open WebUI も Qwen/Qwen3-32B-MLX-4bit なんか使ってると思考部分は隠せているので、gpt-oss (というか Harmony response format) の正式対応が進んでくれたらいいな、と思っています。

単純に思考部分を完全に見えなくするだけであれば、どうせ今回紹介している方法では乱暴にオリジナルのスクリプトを書き換えて使っているので、server.py<|channel|>から<|message|>までのメッセージをクライアントに返さないように改造してしまっても良いかもしれません。

ところで今回どうやって直したか、とか

せっかくなので LM Studio で gpt-oss を動かして協力してもらいながら解決まで持って行きたかったんですが、テストするときには MLX-LM でも gpt-oss をロードする事になりメモリキャパオーバによるクラッシュの危険性が高いので避けました。で、ChatGPT に相談を始めたものの全然解決に近づいている感じがなく時間ばかりがかかりギブアップ。次に Gemini (2.5 Flash) に相談し始めてからはほぼ最短コースで解決にたどり着いた感じです。この時には質問方法や内容に慣れて、深掘りすべきところにもある程度見当が付いてきたこともあったとは思いますが、Gemini を見直しました。

質問の時には、使っている環境、症状の詳細、関係している可能性が高い Python スクリプト全体 (server.py)、サーバのエラー、クライアント (Dify や Open WebUI) のエラー、等を詳細に伝えることで解決できた感じです。ChatGPT はコードの修正をお願いすると全く違うものが出てきたりして使えなかったです。もしかしたら動いたのかも知れませんがとても pull request には使えないものだったので (そういう意味では gpt-oss もそういう用途では使えないのかな)。Gemini は最小限の追加で、コードを差し込むところの説明含め正確でした。

余談ですが、最近プログラマ不要論みたいなのがありますよね。生成 AI で置き換え可能、とかなんとか。確かに最近は 20B~30B 程度のサイズの LLM でもざっくりとしたプロンプトから一発でブロック崩しゲームを書いてくれたりしますが、狙ったとおりの変更やバグの修正などを上手に行うにはプログラムの知識は必要だと思いますね。

おまけ: gpt-oss-20B と Qwen3 30B A3B の SVG 対決

プロンプト: SVG で UFO が牛をさらっている画像を作ってください

(貼ったのは PNG にしたものです)

まずは inferencerlabs/openai-gpt-oss-20b-MLX-6.5bit

文章で説明するのはズルイ。ま、やっちゃダメとも言わんかったか

次に nightmedia/Qwen3-30B-A3B-Thinking-2507-dwq4-mlx

雰囲気がヨイ!けど人さらってますね

現場からは以上となります!

Image by Stable Diffusion (Mochi Diffusion)

リンゴに絆創膏、というイメージで書いてもらいました。バンドエイドは商標ですが、全くそう見えないものができたのでセーフと自己判断して採用。そろそろリンゴ以外を使った方がいいかもと思いつつも結局こんな感じで、生成 AI ばかり使いすぎて頭がアレになってきた人の特徴でしょうかね。

Date:
2025年8月10日 23:07:43

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
small band-aid patches on a red apple

Exclude from Image:

Seed:
1709363568

Steps:
21

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

OpenAI gpt-oss はまだ Mac の MLX-LM と Dify や Open WebUI では正しく動かない (対処法あり)

(2025/08/10 追記) MLX-LM のサーバスクリプトを書き換えて Dify や Open WebUI から使えるようにしました。記事はこちら:

ここ 2日程ローカル LLM 界隈で大騒ぎの OpenAI 初のオープンウェイト大規模言語モデル (LLM) gpt-oss。32GB RAM の Mac Studio で MLX 版 20b モデルを試したところ、Dify と Open WebUI では正しく動きませんでした。思考を思考として正しく認識されず全てが垂れ流しされ、MLX-LM から送られてくる制御文字?か何かで出力が途中で止まります。

Hugging Face から落とした同じモデルを使って、LM Studio (最新の 0.3.22 Build 2) では正しく動作しています。Ollama も対応を表明してますがまだ MLX バックエンドは使えないので、2025年 8月 7日現在限定で言えば、Mac で gpt-oss 使う最適解は LM Studio っぽいですね。

(2025/08/08 追記) LM Studio でサーバを動かし OpenAI-API-compatible のモデルプロバイダとして gpt-oss を登録したところ、Dify でも使えました!思考は丸見えですが、それ以外は問題無さそうです。Python でパーフェクトメイズを作るスクリプトは一発でした。その後色々変更を依頼しても毎回正しく動くスクリプトが生成されます。Dify で見えるトークン出力速度は 70 tok/sec を超えています。ヤバい。

使ったモデル

https://huggingface.co/inferencerlabs/openai-gpt-oss-20b-MLX-6.5bit

「6.5bit はほぼ 8bit と同等の性能 (perplexity)」と書かれてあったので、真に受けて選択。同ページにあるように、VRAM が 17 GB 確保できれば動きます。つまり 32GB 以上の RAM を持った Mac ならそのままで動く計算ですが、VRAM 容量を最適化するには別記事 (↓) をどうぞ。

https://blog.peddals.com/fine-tune-vram-size-of-mac-for-llm
↓ が英語ページの場合は、↑ を開いてください

MLX-LM と MLX のバージョンについての注意点

MLX-LM をバックエンド (OpenAI API コンパチブルサーバ) として使う場合は gpt-oss に対応したバージョン 0.26.3 以上が必要になります。インストール済みの環境で使う場合はアップデートしましょう。

pip install -U mlx-lm

MLX は、新規でインストールする場合は問題ないですが、すでに 0.26.5 より古いバージョンが入っていると、そのままアップデートすると動かなくなります。やっちゃった場合は一度削除してから、再度インストールしましょう。ボクはここで若干ハマりました。

pip uninstall mlx
pip uninstall mlx-metal # うっかり 0.26.5 より古いバージョンからアップデートして入ってしまった場合はアンインストール
pip install mlx mlx-metal

情報源はこちらの issue です:

https://github.com/ml-explore/mlx/issues/2402

2025年 8月 7日現在の最新バージョンはこうなります。

% pip list|grep mlx
mlx 0.28.0
mlx-lm 0.26.2
mlx-metal 0.28.0

参考まで、mlxをアップデートしておかしくなった際にサーバを起動しようとして出たエラーを貼っておきます。同じようにlibmlx.dylib' (no such file)が出た場合は上記のmlxのアンインストール&インストールを実行しましょう。

% mlx_lm.server --host 0.0.0.0 --port 8585 --log-level DEBUG
Traceback (most recent call last):
File "/Users/handsome/Documents/Python/mlx-lm/.venv/bin/mlx_lm.server", line 5, in <module>
from mlx_lm.server import main
File "/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx_lm/__init__.py", line 9, in <module>
from .convert import convert
File "/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx_lm/convert.py", line 7, in <module>
import mlx.core as mx
ImportError: dlopen(/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx/core.cpython-312-darwin.so, 0x0002): Library not loaded: @rpath/libmlx.dylib
Referenced from: <8B6A45F7-00BF-3CEA-9AFF-CD76D4BC76F0> /Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx/core.cpython-312-darwin.so
Reason: tried: '/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx/lib/libmlx.dylib' (no such file), '/Users/distiller/project/build/temp.macosx-14.0-arm64-cpython-312/mlx.core/libmlx.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/distiller/project/build/temp.macosx-14.0-arm64-cpython-312/mlx.core/libmlx.dylib' (no such file), '/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx/lib/libmlx.dylib' (no such file), '/Users/distiller/project/build/temp.macosx-14.0-arm64-cpython-312/mlx.core/libmlx.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/distiller/project/build/temp.macosx-14.0-arm64-cpython-312/mlx.core/libmlx.dylib' (no such file), '/opt/homebrew/lib/libmlx.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/lib/libmlx.dylib' (no such file), '/opt/homebrew/lib/libmlx.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/lib/libmlx.dylib' (no such file)

mlx-lmのバージョンが古い場合は、Dify で gpt-oss を追加する時にERROR - Model type gpt_oss not supportedが出ます。こちらもエラーを貼っておきます。

/Users/handsome/Documents/Python/mlx-lm/.venv/lib/python3.12/site-packages/mlx_lm/server.py:934: UserWarning: mlx_lm.server is not recommended for production as it only implements basic security checks.
warnings.warn(
2025-08-07 18:29:02,619 - INFO - Starting httpd at 0.0.0.0 on port 8585...
2025-08-07 18:29:19,719 - DEBUG - Incoming Request Body: {
"model": "inferencerlabs/openai-gpt-oss-20b-MLX-6.5bit",
"max_tokens": 5,
"messages": [
{
"role": "user",
"content": "ping"
}
]
}
2025-08-07 18:29:19,725 - DEBUG - Starting new HTTPS connection (1): huggingface.co:443
2025-08-07 18:29:19,998 - DEBUG - https://huggingface.co:443 "GET /api/models/inferencerlabs/openai-gpt-oss-20b-MLX-6.5bit/revision/main HTTP/1.1" 200 18528
Fetching 11 files: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 11/11 [00:00<00:00, 223967.69it/s]
2025-08-07 18:29:20,048 - ERROR - Model type gpt_oss not supported.
192.168.111.71 - - [07/Aug/2025 18:29:20] "POST /v1/chat/completions HTTP/1.1" 404 -

その他、MLX-LM を LLM のバックエンドとして使う方法は別記事に書いていますので読んでみてください。Mac で速度と LLM がサポートする大きなコンテキストウィンドウを確保するなら MLX-LM が正解です。

MoE の生成速度のたまらなさ

上記したとおり今日現在 LM Studio でしか正しい動きを確認できていませんが、たまたま最近 Alibaba がリリースした Qwen3 Coder 30B A3B Instruct も gpt-oss 同様 MoE という仕組みで動いています。MoE の細かい内容は他のサイトなどを見てもらうとして、ユーザ目線での最大のメリットは、生成速度の速さです。自分の Mac で動くローカル LLM が、ChatGPT や Gemini 等のクローズド商用モデル同等の速度で文字を生成していく様は、ある意味感動的でもあります。

実は最近、Reasoning/Thinking モデルの精度優先でゆっくりとした生成速度に慣れきった頃に触った Qwen3 Coder 30B A3B Instruct (MoE) の高速生成に感動し何か記事を書こうとしていました。ですが実際に生成されるコードの精度自体がイマイチだったのでどうするか思案していたところ、まさかの OpenAI から gpt-oss がリリースされたのでした。gpt-oss は M2 Max でも 50 token/sec 以上 (!) のスピードでリッチな内容と文字装飾でレスポンスが生成されてくるので、マジたまらないですよ。

まとめ

MLX と Dify や Open WebUI で使えた!とか、使えなかった!という情報が見当たらなかったので、今のところ使えませんでした!という内容でまとめました。 → 使えるようにする方法は、別記事にまとめてます:

リーダーボードなどで無視されがちな GLM-4-32B もプロンプトをしっかり書けば良い結果が得られそうだなぁ、と思っていたところに OpenAI さんがオープンウェイトを出してきたので、他の LLM の細かい話は色々うっちゃって、当面は gpt-oss をいじるのが正解な気がしています。

ボクはいくつかの過去記事で、32GB RAM (ユニファイドメモリ) の Mac でローカル LLM を使うのは苦労と工夫が必要だよ!と書いてきたのですが、なんだかんだと 20B~30B パラメータ程の優秀な LLM が定期的にリリースされているので、とりあえず 32GB RAM の Mac を買えばそれなりに充実したローカル LLM ライフをエンジョイできる!そんな世の中になっていると言えそうです。イェイ!

Image by Stable Diffusion (Mochi Diffusion)

まだ gpt-oss の性能がこれまでのオープンウェイト LLM より大幅に勝っているかどうかわからないのでミスリーディングなあおり画像かもですね。とりあえずみんなかわいかったので採用。

Date:
2025年8月8日 1:05:22

Model:
realisticVision-v51VAE_original_768x512_cn

Size:
768 x 512

Include in Image:
major league baseball player with kids

Exclude from Image:

Seed:
850711837

Steps:
22

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & GPU

© Peddals.com