Mac mini で ローカルLLM： MLX LM を使ってモデルを扱うHTTPサーバーを用意する

## はじめに

これまで、コマンドラインから使ったり Python のコードで処理を行ったりしていた「MLX LM」を、これまでと異なり HTTPサーバーを用意するために使った話です。


### 公式の情報

MLX LM を使ってモデルを扱う HTTPサーバーを用意する方法は、公式の GitHubリポジトリの以下（「HTTP Model Server」の部分）で書かれている内容です。


●mlx-lm/mlx_lm/SERVER.md at main · ml-explore/mlx-lm
　https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/SERVER.md

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1c75bb84-ddac-410a-8d2b-121a7ceaed1a.png)


このページに書かれている以下のコマンドでサーバーを用意できるようです。

```zsh
mlx_lm.server --model <path_to_model_or_hf_repo>
```


## 実際に試してみる

過去のお試しで使った Gemma 3 を使ってみます。

具体的には、MLX LM が使える環境で以下のコマンドを実行しました。

```zsh
mlx_lm.server --model mlx-community/gemma-3-27b-it-qat-3bit
```

以下は上記の実行後の状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/55d272ce-72ce-4e7a-9cd6-546fadba117b.png)

何らかのクライアントから `localhost:8080` でモデルにアクセスできるようです。


:::note warn
【注意】上記のコマンドを実行した際、以下のメッセージが表示されます。この機能を本番環境で使うのはオススメしないという内容です。

> UserWarning: mlx_lm.server is not recommended for production as it only implements basic security checks.
:::


### curl で試す

それでは、公式サンプルとして書かれていた内容をもとに、curl を使う方法でサーバーにアクセスしてみます。

プロンプトだけ書き換えた、以下の内容にしました。

```zsh
curl localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
     "messages": [{"role": "user", "content": "生成AIを8歳に分かるように短い言葉で説明して"}],
     "temperature": 0.7
   }'
```


#### 結果

curl を使った方法で試したところ、以下のレスポンスが得られました。

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_50868_5799e455-d50e-4334-9d8c-e2d561101a66.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1f60478c-96a0-4e95-b287-0bf961014bbc.jpeg)

テキストがエンコードされた状態であるため、そのままでは読めませんでした。


### Node.js で作ったクライアントで試す

もう 1つ、Node.js で作ったクライアントでも試してみます。Node.js で以下のコードを実行しました。


```javascript
const url = "http://localhost:8080/v1/chat/completions";

const payload = {
  messages: [
    { role: "user", content: "生成AIを8歳に分かるように短い言葉で説明して" },
  ],
  temperature: 0.7,
};

(async () => {
  try {
    const res = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });

    if (!res.ok) {
      console.error(`HTTP Error: ${res.status} ${res.statusText}`);
      process.exit(1);
    }

    const data = await res.json();
    console.log(JSON.stringify(data, null, 2));
  } catch (err) {
    console.error("Fetch failed:", err);
    process.exit(1);
  }
})();
```


#### 結果

Node.js で作ったクライアントで得られた結果は以下のとおりです。

冒頭部分は数字の羅列が続く感じの出力になってしまいましたが...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d21faee3-cc5c-49b3-bdff-49054cb00226.png)

その後のところで、日本語テキストの文章が含まれた部分を確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c536cbec-f61d-46e0-b819-a4d21db796e2.png)


レスポンス内に含まれている生成されたテキストは、以下の内容でした。

> "content": "生成AIは、まるで魔法の絵描きさんだよ！\n\n*   みんなが作ったたくさんの絵や文章を読んで、それらをまねっこして、新しい絵や文章を作るんだ。\n*   例えば、「猫の絵を描いて！」って言うと、いろんな猫の絵を自分で考えて作ってくれる！\n*   でも、AIは自分で考えてるわけじゃなくて、過去に見たものを参考にしているだけなんだ。\n*   だから、AIが描いた絵や文章は、ちょっとだけ誰かのものに似ていることもあるんだ。\n\nまるで、いろんな本を読んだ人が、それをもとに新しいお話を作るようなものだよ！\n"



## おわりに

今回、MLX LM を使ってモデルを扱う HTTPサーバーを用意して、そのサーバーへのアクセスを試しました（curl、Node.js を利用）。

今回使ったクライアント側の処理はシンプルなものだったので、さらに別の処理を組み合わせた内容も試せればと思います。




### 【余談】その他

最後に、余談的な内容を少し書いてみます。


#### 公式ページに書かれていたヘルプを呼び出すコマンド

公式ページに書かれていたヘルプを呼び出す以下のコマンドを、試した結果も掲載してみます。

```zsh
mlx_lm.server --help
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f86fbd6d-a491-4364-8369-22c95eded83c.png)


#### MLX-VLM で HTTPサーバー

過去に試したことがある [MLX-VLM も、HTTPサーバーを用意する機能があるようなので](https://github.com/Blaizzy/mlx-vlm/tree/main?tab=readme-ov-file#server-fastapi)、それも試せればと思います。

●Blaizzy/mlx-vlm: MLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.
　https://github.com/Blaizzy/mlx-vlm/tree/main