{"id":50851774,"url":"https://github.com/kiarina/mlx-embedding-server","last_synced_at":"2026-06-14T14:03:20.548Z","repository":{"id":361246019,"uuid":"1253716710","full_name":"kiarina/mlx-embedding-server","owner":"kiarina","description":"Single-purpose HTTP server for multimodal embeddings via mlx-embeddings (Apple Silicon / MLX). Defaults to Qwen3-VL-Embedding-2B.","archived":false,"fork":false,"pushed_at":"2026-06-05T09:17:46.000Z","size":243,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T14:03:18.889Z","etag":null,"topics":["apple-silicon","embeddings","fastapi","mlx","mlx-embeddings","multimodal","qwen3-vl"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kiarina.png","metadata":{"files":{"readme":"README.ja.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T18:39:27.000Z","updated_at":"2026-06-05T09:17:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kiarina/mlx-embedding-server","commit_stats":null,"previous_names":["kiarina/mlx-embedding-server"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kiarina/mlx-embedding-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiarina%2Fmlx-embedding-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiarina%2Fmlx-embedding-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiarina%2Fmlx-embedding-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiarina%2Fmlx-embedding-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kiarina","download_url":"https://codeload.github.com/kiarina/mlx-embedding-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiarina%2Fmlx-embedding-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34324004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-silicon","embeddings","fastapi","mlx","mlx-embeddings","multimodal","qwen3-vl"],"created_at":"2026-06-14T14:03:19.643Z","updated_at":"2026-06-14T14:03:20.542Z","avatar_url":"https://github.com/kiarina.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mlx-embedding-server\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n![Platform: macOS (Apple Silicon)](https://img.shields.io/badge/platform-macOS%20(Apple%20Silicon)-black)\n![Python: 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)\n\n*[English](README.md) | 日本語*\n\n[`mlx-embeddings`](https://github.com/Blaizzy/mlx-embeddings) をラップし、Apple\nSilicon 上で**複数の埋め込みモデル**（テキスト / マルチモーダル）を提供する HTTP\nサーバーです。\n\n単一エンドポイント `POST /v1/embedding` が 1 プロセスで複数モデルを提供し、`model`\nフィールドで使用モデルを選びます。モデルはオンデマンドでロードされ、**サーバー全体の\nメモリ予算**内で常駐します。新しいモデルが収まらない場合は LRU で退避されます。MLX は\nスレッドアフィニティを持つため、プロセス内の単一ワーカーが埋め込みを 1 件ずつ実行し、\n同時リクエストはキューで待機します。\n\n兄弟プロジェクト [mlx-vlm-server](https://github.com/kiarina/mlx-vlm-server)（chat\ncompletions）と同じ「メモリ予算付き・シングルフライト・モデル別ハンドラ」設計を再利用\nしています。\n\n## モデル（デフォルトレジストリ）\n\n| `model`（名前 / エイリアス） | リポジトリ | モダリティ | 次元 |\n|---|---|---|---|\n| `qwen3-embedding-8b`（`text`, `qwen3-embedding`） | `mlx-community/Qwen3-Embedding-8B-mxfp8` | text | 4096 |\n| `qwen3-vl-embedding-2b`（`vl`, `qwen3-vl-embedding`） | `mlx-community/Qwen3-VL-Embedding-2B-mxfp8` | text, image | 2048 |\n\n`GET /v1/models` で現在のレジストリを確認できます。カタログは\n[`registry.py`](mlx_embedding_server/registry.py) にあります。\n\n## 必要環境\n\n- **Apple Silicon（M1 以降）の macOS。** MLX は Apple Silicon GPU でのみ動作します。\n- **Python 3.11+**（`.python-version` で 3.12 を固定）。\n- 依存管理に [uv](https://docs.astral.sh/uv/)。\n- 初回起動時にモデルをダウンロードするため Hugging Face へのネットワークアクセス。\n\n## セットアップと起動\n\n```sh\nuv sync\nuv run mlx-embedding-server\n# または\nuv run python -m mlx_embedding_server\n```\n\n起動時、`MLX_EMBEDDING_WARMUP_MODELS`（デフォルト: **全**登録モデル）に含まれる各モデル\nをロードし、小さなウォームアップ埋め込みで MLX カーネルを温めます。`GET /health` で\n`warm` と常駐モデルを確認できます。\n\n## API\n\n### `POST /v1/embedding`\n\n1 アイテムの埋め込みを計算します。最低 1 つのモダリティ入力が必要です。OpenAI の\n`/v1/embeddings` とは異なり、配列 `input` ではなく**モダリティごとに 1 フィールドを持つ\n単一アイテム**を受け取ります（将来 audio/video へ拡張しやすい形）。\n\n```jsonc\n{\n  \"model\": \"qwen3-vl-embedding-2b\",   // 名前 / エイリアス / リポジトリ。省略でデフォルト\n  \"text\": \"猫の写真\",                  // 任意\n  \"image\": \"\u003cbase64 | data URL | http(s) URL | ローカルパス\u003e\"  // 任意\n}\n```\n\nレスポンス:\n\n```jsonc\n{\n  \"model\": \"qwen3-vl-embedding-2b\",\n  \"embedding\": [0.01, -0.02, ...],\n  \"dimension\": 2048,\n  \"usage\": {\"prompt_tokens\": 0, \"total_tokens\": 0},\n  \"timings\": {\"total_s\": 0.12}\n}\n```\n\n選択したモデルが対応しないモダリティは **400** を返します（例: `qwen3-embedding-8b` に\n`image` を送る → 画像は `qwen3-vl-embedding-2b` を使用）。\n\n### 例\n\n```sh\n# テキスト埋め込み（デフォルトモデル）\ncurl -s http://127.0.0.1:8900/v1/embedding -H 'Content-Type: application/json' \\\n  -d '{\"text\": \"今日はいい天気ですね\"}' | jq '.dimension'\n\n# VL モデルで画像埋め込み（sample.png 同梱）\nIMAGE_B64=$(base64 -i sample.png)\ncurl -s http://127.0.0.1:8900/v1/embedding -H 'Content-Type: application/json' \\\n  -d \"{\\\"model\\\": \\\"vl\\\", \\\"image\\\": \\\"$IMAGE_B64\\\"}\" | jq '.dimension'\n\n# テキスト + 画像\ncurl -s http://127.0.0.1:8900/v1/embedding -H 'Content-Type: application/json' \\\n  -d \"{\\\"model\\\": \\\"vl\\\", \\\"text\\\": \\\"猫\\\", \\\"image\\\": \\\"$IMAGE_B64\\\"}\" | jq '.dimension'\n```\n\n### その他のエンドポイント\n\n- `GET /v1/models` — 登録モデル一覧（capabilities 付き）。\n- `GET /health` — `{status, warm, queue_len, default_model, memory:{loaded, resident_gb, budget_gb}}`。\n- `GET /help` — LLM エージェント向けの機械可読な使用ガイド。\n\n## 設定（環境変数）\n\n| 変数 | デフォルト | 意味 |\n|---|---|---|\n| `MLX_EMBEDDING_MEMORY_LIMIT_GB` | `32` | 常駐モデルのサーバー全体メモリ予算 |\n| `MLX_EMBEDDING_DEFAULT_MODEL` | `qwen3-embedding-8b` | `model` 省略時に使うモデル |\n| `MLX_EMBEDDING_HOST` / `_PORT` | `127.0.0.1` / `8900` | バインドアドレス |\n| `MLX_EMBEDDING_AUTH_TOKEN` | （未設定） | 設定すると `/v1/*` に `Authorization: Bearer \u003ctoken\u003e` を要求 |\n| `MLX_EMBEDDING_WARMUP` | `1` | 起動時ウォームアップ（`0` で無効） |\n| `MLX_EMBEDDING_WARMUP_MODELS` | （全モデル） | 起動時にロード+ウォームするモデル名（カンマ区切り） |\n| `MLX_EMBEDDING_MAX_LENGTH` | `512` | トークナイザに渡す入力の最大トークン長 |\n\n## 設計\n\n- **モデルごとのハンドラが自分の処理フローを持つ。** 各モデルは\n  [`models/`](mlx_embedding_server/models) 配下のモジュールで、`CAPABILITIES` /\n  `load()` / `run()` と任意の `on_load()` を公開します。共通処理（入力解析・レスポンス\n  整形）はただのヘルパ（[`media.py`](mlx_embedding_server/media.py),\n  [`response.py`](mlx_embedding_server/response.py)）。モデル追加 = モジュール 1 つ +\n  レジストリ 1 行。\n- **メモリ予算付き常駐キャッシュ** ([`manager.py`](mlx_embedding_server/manager.py))。\n  使用前に\n  `Σ(常駐weight, target除く) + target.weight + target.peak_headroom ≤ memory_limit_gb`\n  を満たすよう LRU で退避します。退避時は参照を破棄し `gc.collect()` →\n  `mx.clear_cache()`（Metal バッファキャッシュが残るため必須）。`weight_gb` は初回\n  ロード後に実測値で補正されます。\n- **シングルフライト・ワーカー** ([`worker.py`](mlx_embedding_server/worker.py)) — 単一\n  の MLX スレッドがロード・退避・全埋め込みを実行し、予算計算をレースフリーにします。\n- Qwen3-VL ハンドラは、変換済み processor に欠けている `chat_template` / token-id を\n  `on_load` で補填します（既知の Qwen3-VL の癖）。\n\n## サービスとして実行（launchd / mise 経由）\n\n```sh\nmise run service:install   # ~/Library/LaunchAgents/io.github.kiarina.mlx-embedding-server.plist を作成\nmise run service:start\nmise run service:status\nmise run service:stop\nmise run service:uninstall\n```\n\n`mise run logs:out` / `logs:err` でサービスログを追跡できます。\n\n## ライセンス\n\nMIT — [LICENSE](LICENSE) を参照。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiarina%2Fmlx-embedding-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkiarina%2Fmlx-embedding-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiarina%2Fmlx-embedding-server/lists"}