Jesse A. Green / notebook

oMLX — Administrator & Developer Guide

Generated May 2026 from jundot/omlx at f4ed78d

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Audience: System administrators, DevOps engineers, and developers deploying, operating, extending, or contributing to oMLX. Version: 0.3.8.dev1 | Apache-2.0 | macOS 15.0+ / Apple Silicon (M1+) / Python 3.10+


Table of Contents

  1. Architecture Overview
  2. System Requirements
  3. Configuration Reference
  4. Deployment & Scaling
  5. Operations
  6. Performance
  7. Security
  8. Developer Internals
  9. Integration Points

1. Architecture Overview

oMLX is a production-grade, OpenAI-compatible multi-model LLM inference server built exclusively for Apple Silicon. The system leverages Apple's MLX framework for unified-memory GPU acceleration and implements a vLLM-style continuous batching architecture adapted for the Metal/MLX runtime.

1.1 High-Level System Diagram

┌──────────────────────────────────────────────────────────────┐
│                        Client Layer                          │
│   OpenAI SDK / Anthropic SDK / Codex / curl / Admin UI       │
└─────────────────────────┬────────────────────────────────────┘
                          │ HTTP / SSE / WebSocket
┌─────────────────────────▼────────────────────────────────────┐
│                   FastAPI Server (server.py)                  │
│  /v1/chat/completions  /v1/messages  /v1/embeddings           │
│  /v1/rerank  /v1/responses  /v1/audio/*  /health             │
│                  Admin Dashboard (/admin/*)                   │
└─────────────────────────┬────────────────────────────────────┘
                          │
┌─────────────────────────▼────────────────────────────────────┐
│                    Engine Pool (engine_pool.py)               │
│   LRU Model Eviction │ Memory Tracking │ Model Discovery      │
│   Model Types: llm, vlm, embedding, reranker, audio_*        │
└──────┬──────────┬───────────┬───────────┬────────────────────┘
       │          │           │           │
  ┌────▼───┐ ┌───▼────┐ ┌────▼───┐ ┌────▼────────┐
  │Batched │ │  VLM   │ │Embed-  │ │  Audio      │
  │Engine  │ │Engine  │ │ding    │ │ STT/TTS/STS │
  └────┬───┘ └───┬────┘ │Engine  │ └─────────────┘
       │         │      └────────┘
┌──────▼─────────▼──────────────────────────────────────────────┐
│                   Engine Core (engine_core.py)                 │
│   Single MLX Executor Thread (ThreadPoolExecutor max_workers=1)│
│   Prevents Metal command buffer races                          │
└─────────────────────────┬──────────────────────────────────────┘
                          │
┌─────────────────────────▼──────────────────────────────────────┐
│                  Scheduler (scheduler.py)                       │
│   Continuous Batching │ Prefill/Decode Phases │ KV Allocation  │
│   mlx-lm BatchGenerator                                        │
└──────────────┬──────────────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────────────┐
│                    Cache Subsystem (cache/)                      │
│  PagedCacheManager → BlockAwarePrefixCache → TieredCacheManager │
│  PagedSSDCacheManager → VisionFeatureSSDCache                   │
│  BoundarySnapshotSSDStore → CacheRecoveryManager                │
└─────────────────────────────────────────────────────────────────┘

Note

The single MLX executor thread is a deliberate architectural constraint. All GPU operations are serialized through a ThreadPoolExecutor(max_workers=1) to prevent Metal command buffer races — a hard requirement imposed by the MLX framework's threading model. Do not attempt to parallelize GPU calls; use the batch scheduler instead.

1.2 Component Inventory

Component Module Role
CLI omlx/cli.py Entry point; serve, launch, diagnose subcommands
FastAPI Server omlx/server.py 102 HTTP endpoints across inference, admin, audio, MCP
Engine Pool omlx/engine_pool.py Multi-model LRU manager; model discovery, load/unload
Engine Core omlx/engine_core.py Async inference loop; single-thread GPU serialization
Scheduler omlx/scheduler.py Continuous batching; prefill/decode; KV allocation
Cache — Paged omlx/cache/paged_cache.py Block-based in-memory KV cache with LRU eviction
Cache — Prefix omlx/cache/prefix_cache.py Block-aware prefix sharing for repeated prompt segments
Cache — SSD Paged omlx/cache/paged_ssd_cache.py SSD persistence for KV blocks; async writer thread
Cache — Boundary omlx/cache/boundary_ssd.py SSD offload for boundary cache snapshots
Cache — Vision SSD omlx/cache/vision_ssd.py SSD cache for VLM vision features
Cache — Tiered omlx/cache/tiered.py Orchestrates hot (GPU) + cold (SSD) cache tiers
Cache — Hybrid omlx/cache/hybrid.py Per-layer cache type configuration
Cache — Recovery omlx/cache/recovery.py Crash recovery for SSD cache state
LLM Model omlx/models/llm.py MLX language model wrapper (load, generate, stream)
Embedding Model omlx/models/embedding.py XLMRoBERTa/BERT native + mlx-embeddings fallback
Reranker Model omlx/models/reranker.py CausalLM, Jina, SequenceClassification architectures
VLM Adapter omlx/models/vlm.py mlx-vlm wrapper for vision-language models
Batched Engine omlx/engine/batched.py Continuous batching for LLMs
VLM Engine omlx/engine/vlm_batched.py Vision-language model engine
DFlash Engine omlx/engine/dflash.py DFlash attention engine
Embedding Engine omlx/engine/embedding.py Batch embedding generation
Reranker Engine omlx/engine/reranker.py Document reranking
STT Engine omlx/engine/stt.py Speech-to-text via mlx-audio
TTS Engine omlx/engine/tts.py Text-to-speech via mlx-audio
STS Engine omlx/engine/sts.py Speech-to-speech via mlx-audio
API — OpenAI omlx/api/openai_models.py OpenAI-format Pydantic models
API — Anthropic omlx/api/anthropic_models.py, anthropic_utils.py Anthropic Messages API conversion
API — Responses omlx/api/responses_models.py, responses_utils.py Responses API with in-memory store
Tool Calling omlx/api/tool_calling.py Qwen/Llama/Gemma format tool call parsing
Thinking omlx/api/thinking.py <think> tag extraction for reasoning models
Grammar omlx/api/grammar.py Structured output via xgrammar
MCP Client omlx/mcp/client.py Individual MCP server connections (stdio/SSE/HTTP)
MCP Manager omlx/mcp/manager.py Multi-server MCP connection management
MCP Executor omlx/mcp/executor.py Tool call execution through MCP
Admin Routes omlx/admin/routes.py 80+ admin API endpoints
Admin Auth omlx/admin/auth.py Session auth with API key and sub-keys
Benchmarks omlx/admin/benchmark.py Throughput benchmarking
Accuracy Evals omlx/admin/accuracy_benchmark.py MMLU, HellaSwag, GSM8K, HumanEval, etc.
HF Downloader omlx/admin/hf_downloader.py HuggingFace model download task manager
MS Downloader omlx/admin/ms_downloader.py ModelScope model download task manager
HF Uploader omlx/admin/hf_uploader.py HuggingFace model upload task manager
oQ Manager omlx/admin/oq_manager.py oQ quantization task management
Settings omlx/settings.py GlobalSettings backed by ~/.omlx/settings.json
Config omlx/config.py Dataclass-based typed configuration
Exceptions omlx/exceptions.py Rich exception hierarchy (363 classes, 1431 functions)
Memory Monitor omlx/memory_monitor.py Memory pressure detection
Process Enforcer omlx/process_memory_enforcer.py Process memory limit enforcement
Model Discovery omlx/model_discovery.py Auto-discovery from directories
Model Settings omlx/model_settings.py Per-model settings with profiles
Model Profiles omlx/model_profiles.py Named setting profiles and templates
Model Registry omlx/model_registry.py Model ownership tracking (prevents concurrent engine use)
Logging omlx/logging_config.py Structured logging, rotation, request context, colored output
Optimizations omlx/optimizations.py Performance optimizations
oQ Quantization omlx/oq.py Level-based quantization with calibration data
TurboQuant KV omlx/turboquant_kv.py KV cache quantization (int8/int4)
Prefill Progress omlx/prefill_progress.py Prefill progress tracking
Server Metrics omlx/server_metrics.py Metrics collection
Output Collector omlx/output_collector.py Low-latency output collection (vLLM pattern)
Harmony Adapter omlx/adapter/harmony.py Harmony (gpt-oss) streaming format parsing
Gemma4 Adapter omlx/adapter/gemma4.py Gemma 4 reasoning channel parsing
Output Parser omlx/adapter/output_parser.py Generic output parsing interface
Patch — GatedDelta omlx/patches/gated_delta_advance.py Fix for qwen3_5 GatedDeltaNet missing cache.advance()
Patch — IndexCache omlx/patches/index_cache.py Index cache optimization
Patch — SpecPrefill omlx/patches/specprefill.py Attention-score-based context pruning
Patch — TurboQuant omlx/patches/turboquant_attention.py TurboQuant KV cache quantization
Eval Suite omlx/eval/ 16 benchmarks: MMLU, MMLU-Pro, KMMLU, CMMLU, JMMLU, HellaSwag, TruthfulQA, ARC-Challenge, Winogrande, GSM8K, MathQA, HumanEval, MBPP, LiveCodeBench, BBQ, SafetyBench
macOS App packaging/ PyObjC menubar app; DMG via venvstacks
Homebrew Formula/omlx.rb Homebrew formula with optional --with-grammar

1.3 Request Data Flow

Client Request
    │
    ▼
FastAPI Server (server.py)
    │  verify_api_key middleware
    │  resolve model from request body
    ▼
EnginePool.get_or_load(model_id)
    │  LRU eviction if memory pressure
    │  Load model from disk if not resident
    ▼
Engine dispatch (type-based routing)
    │  BatchedEngine (LLM)
    │  VLMBatchedEngine (VLM)
    │  EmbeddingEngine / RerankerEngine
    │  STT/TTS/STSEngine (audio)
    ▼
EngineCore async loop
    │  scheduler.step() called on MLX executor thread
    ▼
Scheduler (scheduler.py)
    │  Prefill phase → BatchGenerator (mlx-lm)
    │  Decode phase → PagedCacheManager
    │  Prefix cache lookup → BlockAwarePrefixCache
    │  SSD offload → PagedSSDCacheManager
    ▼
RequestOutput → OutputCollector
    │
    ▼
StreamingResponse (SSE) or JSONResponse → Client

1.4 Repository Layout

omlx/
├── omlx/                   # Main package
│   ├── api/                # API models and adapters (OpenAI, Anthropic, Responses)
│   │   ├── adapters/       # SSE formatter, OpenAI adapter, Anthropic adapter
│   │   ├── openai_models.py
│   │   ├── anthropic_models.py / anthropic_utils.py
│   │   ├── responses_models.py / responses_utils.py
│   │   ├── tool_calling.py
│   │   ├── thinking.py
│   │   ├── grammar.py
│   │   ├── embedding_models.py / embedding_utils.py
│   │   ├── rerank_models.py
│   │   ├── audio_routes.py
│   │   └── mcp_routes.py
│   ├── cache/              # KV cache management
│   ├── engine/             # Inference engines
│   ├── mcp/                # Model Context Protocol integration
│   ├── models/             # Model wrappers
│   ├── integrations/       # External tool integrations (Codex, OpenCode, etc.)
│   ├── admin/              # Admin dashboard, benchmarks, downloaders
│   │   └── templates/      # Jinja2 HTML (dashboard, chat, login, settings, etc.)
│   ├── adapter/            # Output format adapters
│   ├── patches/            # Runtime monkey-patches
│   ├── eval/               # 16 benchmark implementations
│   ├── utils/              # Hardware, image, tokenizer utilities
│   ├── server.py           # FastAPI application
│   ├── cli.py              # CLI entry point
│   ├── scheduler.py        # Continuous batching scheduler
│   ├── engine_core.py      # Core async inference engine
│   ├── engine_pool.py      # Multi-model engine management
│   ├── settings.py         # GlobalSettings (JSON-backed)
│   ├── config.py           # Typed dataclass configuration
│   ├── exceptions.py       # Exception hierarchy
│   └── ...
├── packaging/              # macOS menubar app (PyObjC + venvstacks)
├── Formula/                # Homebrew formula (omlx.rb)
├── tests/                  # Test suite (pytest)
└── docs/                   # Documentation

2. System Requirements

2.1 Hardware Requirements

Component Minimum Recommended
CPU / SoC Apple M1 (any variant) Apple M2 Pro / M3 Max / M4 Max
Unified Memory 8 GB 32 GB+
Storage 20 GB free (models excluded) NVMe SSD, 500 GB+
macOS 15.0 (Sequoia) Latest stable release

Note

oMLX exclusively targets Apple Silicon. There is no x86/Intel path and no NVIDIA/CUDA support. All GPU operations use the Apple Metal backend via the MLX framework.

2.2 Software Requirements

Dependency Version Notes
macOS 15.0+ Sequoia required; earlier versions unsupported
Python 3.10+ 3.11 recommended (Homebrew formula pins 3.11)
mlx-lm Latest Core LLM inference
mlx-vlm Latest Vision-language models
mlx-audio Latest Audio model support (STT/TTS/STS)
mlx-embeddings Latest Fallback embedding support
FastAPI + uvicorn Latest HTTP server
xgrammar Optional Structured output / constrained generation
PyObjC Optional macOS menubar app (packaging/)
Rust toolchain Build only Required for Homebrew build

2.3 Installation Methods

Homebrew (recommended for production):

brew tap jundot/omlx
brew install omlx

# With structured output support (xgrammar):
brew install omlx --with-grammar

# Run as a background service:
brew services start omlx

pip (development):

pip install omlx
# or from source:
git clone https://github.com/jundot/omlx
cd omlx
pip install -e ".[dev]"

macOS App: Download the DMG from the releases page. The menubar app manages server lifecycle, preferences, and auto-updates via PyObjC + venvstacks.


3. Configuration Reference

3.1 Settings Priority

CLI arguments  >  environment variables  >  ~/.omlx/settings.json  >  built-in defaults

Important

Settings loaded from ~/.omlx/settings.json are persistent across restarts. Environment variables override JSON settings but are not persisted. CLI arguments are ephemeral and highest priority. Always verify the effective configuration via GET /api/global-settings after startup.

3.2 CLI Reference

omlx serve — Start the inference server

omlx serve [OPTIONS]

Core:
  --host TEXT                   Bind host (default: 127.0.0.1)
  --port INT                    Bind port (default: 8080)
  --log-level TEXT              Log level: debug|info|warning|error (default: info)
  --model TEXT                  Default model to load at startup
  --model-dir TEXT              Directory to scan for models

Memory:
  --max-model-memory INT        Maximum memory (MB) for model weights
  --max-process-memory INT      Hard process memory ceiling (MB)

Concurrency:
  --max-concurrent-requests INT Maximum simultaneous inference requests
                                (alias: --max-num-seqs)

Cache:
  --cache-enabled BOOL          Enable KV cache (default: true)
  --ssd-cache-dir TEXT          Path for SSD KV cache storage
  --ssd-cache-max-size INT      Maximum SSD cache size (MB)
  --initial-cache-blocks INT    Initial number of KV cache blocks to allocate
  --hot-cache-only BOOL         Disable SSD offload, keep cache in GPU memory only
  --paged-ssd-cache-dir TEXT    Path for paged SSD KV cache
  --paged-ssd-cache-max-size INT Maximum paged SSD cache size (MB)

Inference:
  --max-tokens INT              Default maximum tokens per response
  --temperature FLOAT           Default sampling temperature
  --continuous-batching BOOL    Enable continuous batching (default: true)
  --trust-remote-code BOOL      Allow execution of remote model code

Auth:
  --api-key TEXT                API key for authentication

MCP:
  --mcp-config TEXT             Path to MCP server config (JSON or YAML)

Network:
  --http-proxy TEXT             HTTP proxy URL
  --https-proxy TEXT            HTTPS proxy URL
  --no-proxy TEXT               Comma-separated hostnames to bypass proxy
  --ca-bundle TEXT              Path to CA bundle for TLS verification
  --hf-endpoint TEXT            HuggingFace Hub endpoint override
  --ms-endpoint TEXT            ModelScope endpoint override

Logging:
  --log-dir TEXT                Directory for log file output
  --log-retention-days INT      Days to retain log files

omlx launch — Launch server with model (convenience wrapper)

omlx launch MODEL_ID [OPTIONS]
  All options from `omlx serve` apply; model is positional.

omlx diagnose — System diagnostics

omlx diagnose
  Prints hardware info, Python environment, MLX version, detected models,
  memory availability, and connectivity checks. No server required.

3.3 Environment Variables

All 35 environment variables recognized by oMLX:

Variable Type Default Description
OMLX_HOST string 127.0.0.1 Bind host for the HTTP server
OMLX_PORT int 8080 Bind port for the HTTP server
OMLX_LOG_LEVEL string info Log verbosity: debug, info, warning, error
OMLX_MODEL string — Default model ID to load at startup
OMLX_MODEL_DIR string — Directory scanned for model discovery
OMLX_MAX_MODEL_MEMORY int — Maximum memory (MB) for model weights
OMLX_MAX_PROCESS_MEMORY int — Hard process memory ceiling (MB)
OMLX_MAX_CONCURRENT_REQUESTS int — Max simultaneous inference requests
OMLX_MAX_NUM_SEQS int — Alias for OMLX_MAX_CONCURRENT_REQUESTS
OMLX_CACHE_ENABLED bool true Enable/disable KV cache
OMLX_SSD_CACHE_DIR string — Path for SSD KV cache storage
OMLX_SSD_CACHE_MAX_SIZE int — Maximum SSD cache size in MB
OMLX_INITIAL_CACHE_BLOCKS int — Initial KV cache block count
OMLX_HOT_CACHE_ONLY bool false Disable SSD offload; keep all cache in GPU RAM
OMLX_API_KEY string — API key for request authentication
OMLX_MCP_CONFIG string — Path to MCP server configuration file
OMLX_MAX_TOKENS int — Default maximum tokens per response
OMLX_TEMPERATURE float — Default sampling temperature
OMLX_TRUST_REMOTE_CODE bool false Allow remote model code execution
OMLX_CONTINUOUS_BATCHING bool true Enable continuous batching scheduler
OMLX_PAGED_SSD_CACHE_DIR string — Path for paged SSD KV cache
OMLX_PAGED_SSD_CACHE_MAX_SIZE int — Maximum paged SSD cache size in MB
OMLX_HF_ENDPOINT string https://huggingface.co HuggingFace Hub endpoint (override for mirrors)
OMLX_MS_ENDPOINT string — ModelScope endpoint override
OMLX_HTTP_PROXY string — HTTP proxy URL
OMLX_HTTPS_PROXY string — HTTPS proxy URL
OMLX_NO_PROXY string — Comma-separated proxy bypass hostnames
OMLX_CA_BUNDLE string — Path to custom CA certificate bundle
OMLX_LOG_DIR string — Directory for log file rotation
OMLX_LOG_RETENTION_DAYS int — Days to retain rotated log files
OMLX_SECRET_KEY string — Secret key for session signing (admin auth)
MODELSCOPE_DOMAIN string — ModelScope domain override
DFLASH_MAX_CTX int — Maximum context length for DFlash attention engine

3.4 Settings File Reference (~/.omlx/settings.json)

The settings file is a JSON document with named sections. Unset keys fall back to built-in defaults.

{
  "server": {
    "host": "127.0.0.1",
    "port": 8080,
    "log_level": "info",
    "secret_key": "<random-string>"
  },
  "model": {
    "default_model": "mlx-community/Mistral-7B-Instruct-v0.3-4bit",
    "model_dir": "/Users/you/models",
    "trust_remote_code": false
  },
  "memory": {
    "max_model_memory": null,
    "max_process_memory": null
  },
  "cache": {
    "enabled": true,
    "initial_blocks": 512,
    "hot_cache_only": false,
    "ssd_cache_dir": null,
    "ssd_cache_max_size": null,
    "paged_ssd_cache_dir": null,
    "paged_ssd_cache_max_size": null
  },
  "auth": {
    "api_key": null
  },
  "huggingface": {
    "endpoint": "https://huggingface.co",
    "token": null
  },
  "modelscope": {
    "endpoint": null,
    "token": null
  },
  "network": {
    "http_proxy": null,
    "https_proxy": null,
    "no_proxy": null,
    "ca_bundle": null
  },
  "mcp": {
    "config": null
  },
  "logging": {
    "log_dir": null,
    "log_retention_days": 7
  }
}

Manage settings at runtime via the admin API:

# Read current effective settings
curl http://localhost:8080/api/global-settings

# Update settings (persists to settings.json)
curl -X POST http://localhost:8080/api/global-settings \
  -H "Content-Type: application/json" \
  -d '{"server": {"log_level": "debug"}}'

3.5 Typed Configuration Classes (omlx/config.py)

The internal dataclass hierarchy used for validated runtime configuration:

Class Fields
ServerConfig host, port, log_level, secret_key
ModelConfig default_model, model_dir, trust_remote_code
GenerationConfig max_tokens, temperature, top_p, repetition_penalty
SchedulerConfig max_num_seqs, continuous_batching, max_batch_size
PagedSSDCacheConfig dir, max_size_mb, block_size, async_writer
MCPConfig config_path, servers (list)
OMLXConfig server, model, generation, scheduler, cache, mcp

3.6 Per-Model Settings and Profiles

Per-model settings override global defaults for specific models. Profiles are named preset collections:

# Set per-model overrides
curl -X PUT http://localhost:8080/api/models/{model_id}/settings \
  -d '{"max_tokens": 4096, "temperature": 0.7}'

# Create a profile
curl -X POST http://localhost:8080/api/models/{model_id}/profiles \
  -d '{"name": "creative", "settings": {"temperature": 1.2, "top_p": 0.95}}'

# Apply a profile
curl -X POST http://localhost:8080/api/models/{model_id}/profiles/creative/apply

# Manage global profile templates
curl http://localhost:8080/api/profile-templates
curl -X POST http://localhost:8080/api/profile-templates \
  -d '{"name": "precise", "settings": {"temperature": 0.1}}'

3.7 MCP Configuration

MCP (Model Context Protocol) servers are configured via a JSON or YAML file referenced by OMLX_MCP_CONFIG or --mcp-config:

{
  "servers": [
    {
      "name": "filesystem",
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"]
    },
    {
      "name": "remote-tools",
      "transport": "sse",
      "url": "https://mcp.example.com/sse"
    },
    {
      "name": "http-tools",
      "transport": "streamable-http",
      "url": "https://tools.example.com/mcp"
    }
  ]
}

4. Deployment & Scaling

4.1 Starting the Server

Basic start:

omlx serve --host 0.0.0.0 --port 8080

With a specific model and auth:

omlx serve \
  --model mlx-community/Llama-3.2-3B-Instruct-4bit \
  --api-key "your-secret-key" \
  --host 0.0.0.0 \
  --port 8080

Via Homebrew service (background daemon):

brew services start omlx
brew services stop omlx
brew services restart omlx
# View logs:
tail -f $(brew --prefix)/var/log/omlx.log

Via macOS menubar app: Install the DMG. The menubar app starts/stops the server, shows status, and opens the admin dashboard.

4.2 Multi-Model Configuration

oMLX supports loading multiple models simultaneously, bounded by available unified memory. Models are managed by the Engine Pool with LRU eviction:

# Start with a model directory — all models discovered automatically
omlx serve --model-dir /Users/you/mlx-models

# The engine pool loads models on demand. Unload explicitly:
curl -X POST http://localhost:8080/v1/models/model-id/unload

# Check what is currently loaded:
curl http://localhost:8080/v1/models

Engine Pool memory management:

  • When a new model is requested and memory is insufficient, the LRU (least recently used) loaded model is evicted.
  • OMLX_MAX_MODEL_MEMORY sets the ceiling for total model weight memory.
  • OMLX_MAX_PROCESS_MEMORY is a hard process-level ceiling enforced by process_memory_enforcer.py.
  • Monitor current memory state via GET /api/device-info and GET /api/stats.

4.3 Model Types and Engine Routing

The Engine Pool routes model IDs to the correct engine type at load time based on model architecture detection:

Model Type Engine Endpoint(s)
llm BatchedEngine /v1/completions, /v1/chat/completions
vlm VLMBatchedEngine /v1/chat/completions (with image inputs)
embedding EmbeddingEngine /v1/embeddings
reranker RerankerEngine /v1/rerank
audio_stt STTEngine /v1/audio/transcriptions
audio_tts TTSEngine /v1/audio/speech
audio_sts STSEngine /v1/audio/process

4.4 Homebrew Formula Details

The Homebrew formula (Formula/omlx.rb) specifics:

  • Build dependency: rust (compile-time only)
  • Runtime dependencies: python@3.11, macOS arm64
  • Optional: --with-grammar installs xgrammar for structured output support
  • Resources: mlx-audio (with patched mlx-lm version pin)
  • Service support: brew services start omlx installs a launchd plist

4.5 Reverse Proxy / Production Deployment

For production deployments with TLS and authentication:

# nginx example
server {
    listen 443 ssl;
    server_name inference.example.com;

    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # Required for SSE streaming:
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 300s;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

Important

When running behind a reverse proxy, ensure proxy_buffering off for Server-Sent Events (SSE) streaming responses. Buffering breaks streaming token delivery to clients.


5. Operations

5.1 API Endpoints — Full Reference

All 102 endpoints organized by category.

Inference API

Method Path Auth Description
GET /health None Server health check
GET /api/status None Detailed server status
GET /v1/models Optional List available models
GET /v1/models/status Optional Model load status
POST /v1/models/{model_id}/unload API Key Unload model from memory
POST /v1/completions API Key Text completions (OpenAI)
POST /v1/chat/completions API Key Chat completions (OpenAI)
POST /v1/messages API Key Anthropic Messages API
POST /v1/messages/count_tokens API Key Token counting (Anthropic)
POST /v1/responses API Key Responses API (Codex)
GET /v1/responses/{response_id} API Key Retrieve stored response
DELETE /v1/responses/{response_id} API Key Delete stored response
POST /v1/embeddings API Key Generate embeddings (OpenAI)
POST /v1/rerank API Key Document reranking
POST /v1/audio/transcriptions API Key Speech-to-text
POST /v1/audio/speech API Key Text-to-speech
POST /v1/audio/process API Key Speech-to-speech

Admin API — Authentication & Account

Method Path Auth Description
POST /api/login None Authenticate with API key
POST /api/setup-api-key None First-run API key setup
POST /api/logout Session End session
GET /auto-login None Cookie-based auto-login
POST /api/sub-keys Session Create sub-API key
DELETE /api/sub-keys Session Delete sub-API key

Admin API — Models

Method Path Auth Description
GET /api/models Session List all discovered models
POST /api/models/{id}/unload Session Unload model from memory
POST /api/models/{id}/load Session Load model into memory
POST /api/reload Session Reload all active models
PUT /api/models/{id}/settings Session Update model settings
GET /api/models/{id}/generation_config Session Get generation config
GET /api/models/{id}/profiles Session List model profiles
POST /api/models/{id}/profiles Session Create model profile
PUT /api/models/{id}/profiles/{name} Session Update model profile
DELETE /api/models/{id}/profiles/{name} Session Delete model profile
POST /api/models/{id}/profiles/{name}/apply Session Apply profile as active settings
GET /api/profile-templates Session List global profile templates
POST /api/profile-templates Session Create global profile template
PUT /api/profile-templates/{name} Session Update global profile template
DELETE /api/profile-templates/{name} Session Delete global profile template
POST /api/presets/refresh Session Refresh preset list from disk

Admin API — Server & Monitoring

Method Path Auth Description
GET /api/server-info Session Server version, uptime, config
GET /api/global-settings Session Read current settings.json
POST /api/global-settings Session Update and persist settings.json
GET /api/logs Session Tail server log output
GET /api/stats Session Per-model inference statistics
POST /api/stats/clear Session Clear stats for current session
POST /api/stats/clear-alltime Session Clear all-time statistics
GET /api/device-info Session Apple Silicon device info + memory
GET /api/update-check Session Check for oMLX updates
POST /api/cache/probe Session Probe cache state
POST /api/ssd-cache/clear Session Clear SSD KV cache
GET /api/grammar/parsers Session List active xgrammar parsers

Admin API — HuggingFace Model Management

Method Path Auth Description
GET /api/hf/recommended Session Recommended models from HF Hub
GET /api/hf/search Session Search HuggingFace Hub
GET /api/hf/model-info Session Model metadata from HF Hub
GET /api/hf/models Session Locally downloaded HF models
POST /api/hf/download Session Start model download task
GET /api/hf/tasks Session List download tasks
POST /api/hf/cancel/{id} Session Cancel download task
POST /api/hf/retry/{id} Session Retry failed download
DELETE /api/hf/task/{id} Session Remove download task record
DELETE /api/hf/models/{name} Session Delete local model files

Admin API — ModelScope Model Management

Method Path Auth Description
GET /api/ms/status Session ModelScope connectivity status
GET /api/ms/recommended Session Recommended models from ModelScope
GET /api/ms/search Session Search ModelScope registry
GET /api/ms/model-info Session Model metadata from ModelScope
POST /api/ms/download Session Start model download from ModelScope
GET /api/ms/tasks Session List ModelScope download tasks
POST /api/ms/cancel/{id} Session Cancel ModelScope download
POST /api/ms/retry/{id} Session Retry failed ModelScope download
DELETE /api/ms/task/{id} Session Remove ModelScope task record

Admin API — Benchmarks

Method Path Auth Description
POST /api/bench/start Session Start throughput benchmark
GET /api/bench/{id}/stream Session Stream benchmark progress (SSE)
POST /api/bench/{id}/cancel Session Cancel running benchmark
GET /api/bench/{id}/results Session Get benchmark results
POST /api/bench/accuracy/queue/add Session Queue accuracy benchmark task
GET /api/bench/accuracy/queue/status Session Accuracy benchmark queue status
DELETE /api/bench/accuracy/queue/{idx} Session Remove task from accuracy queue
GET /api/bench/accuracy/results Session All accuracy benchmark results
POST /api/bench/accuracy/results/reset Session Reset accuracy results
POST /api/bench/accuracy/cancel Session Cancel running accuracy benchmark
GET /api/bench/accuracy/{id}/stream Session Stream accuracy benchmark (SSE)

Admin API — Quantization & Upload

Method Path Auth Description
GET /api/oq/models Session Models eligible for oQ quantization
GET /api/oq/estimate Session Estimate oQ quantization time
POST /api/oq/start Session Start oQ quantization task
GET /api/oq/tasks Session List quantization tasks
POST /api/oq/cancel/{id} Session Cancel quantization task
DELETE /api/oq/task/{id} Session Remove quantization task record
POST /api/upload/validate-token Session Validate HF upload token
GET /api/upload/oq-models Session Models ready for upload
POST /api/upload/start Session Start HuggingFace upload task
GET /api/upload/tasks Session List upload tasks
POST /api/upload/cancel/{id} Session Cancel upload task
DELETE /api/upload/task/{id} Session Remove upload task record

MCP Tool Endpoints

Method Path Auth Description
GET /tools None List all MCP tools
GET /servers None List MCP server connections
POST /execute API Key Execute MCP tool call

5.2 Health and Monitoring

# Quick health check
curl http://localhost:8080/health
# Response: {"status": "ok"}

# Detailed status
curl http://localhost:8080/api/status

# Per-model statistics
curl http://localhost:8080/api/stats

# Device info (memory usage, SoC details)
curl http://localhost:8080/api/device-info

# Server information
curl http://localhost:8080/api/server-info

Note

The /health endpoint requires no authentication and is suitable for load-balancer health checks or uptime monitors. The /api/status endpoint returns more detail but also requires no auth.

5.3 Log Management

oMLX uses structured logging with rotation (omlx/logging_config.py):

# Configure log directory and retention:
omlx serve --log-dir /var/log/omlx --log-retention-days 14

# Or via environment:
OMLX_LOG_DIR=/var/log/omlx OMLX_LOG_RETENTION_DAYS=14 omlx serve

# Stream logs via admin API (last N lines):
curl http://localhost:8080/api/logs

# Features:
# - Rotating file handler with configurable retention
# - Request context injection (request_id, model_id per line)
# - Colored terminal output for development
# - Structured JSON format option for log aggregation

5.4 Model Discovery

oMLX auto-discovers models from directories using omlx/model_discovery.py. A directory is recognized as a model if it contains:

  • config.json (model architecture configuration)
  • tokenizer.json or tokenizer_config.json
  • MLX weight files (*.safetensors or *.npz)
# Point to a directory:
omlx serve --model-dir /Users/you/mlx-models

# Trigger a manual discovery refresh:
curl -X POST http://localhost:8080/api/presets/refresh

5.5 Model Ownership and Registry

The ModelRegistry (omlx/model_registry.py) prevents concurrent engine use of the same model. When a model is claimed by an engine, subsequent load attempts for the same model ID will block until the owner releases it. This prevents race conditions during parallel request processing.


6. Performance

6.1 Continuous Batching

oMLX implements vLLM-style continuous batching via the Scheduler and EngineCore. Key parameters:

# Enable/disable continuous batching (default: enabled)
OMLX_CONTINUOUS_BATCHING=true omlx serve

# Set maximum concurrent sequences in a batch
OMLX_MAX_CONCURRENT_REQUESTS=8 omlx serve

The scheduler manages prefill and decode phases independently, allowing new requests to join ongoing decode batches. This maximizes GPU utilization compared to static batching.

6.2 KV Cache Architecture

The cache subsystem has six layers:

Layer Class Location Purpose
L1 — Paged PagedCacheManager GPU (unified memory) Block-based KV cache with LRU eviction
L2 — Prefix BlockAwarePrefixCache GPU (unified memory) Prefix block sharing across requests
L3 — Tiered TieredCacheManager GPU (hot) + SSD (cold) Orchestrates hot/cold promotion
L4 — Paged SSD PagedSSDCacheManager NVMe SSD Async-write paged KV persistence
L5 — Boundary SSD BoundarySnapshotSSDStore NVMe SSD Boundary cache snapshot offload
L6 — Vision SSD VisionFeatureSSDCache NVMe SSD VLM vision feature persistence
# Configure SSD cache:
omlx serve \
  --ssd-cache-dir ~/.omlx/cache \
  --ssd-cache-max-size 51200 \
  --paged-ssd-cache-dir ~/.omlx/paged-cache \
  --paged-ssd-cache-max-size 102400 \
  --initial-cache-blocks 1024

# Disable SSD offload (pure GPU cache):
omlx serve --hot-cache-only true

# Clear SSD cache at runtime:
curl -X POST http://localhost:8080/api/ssd-cache/clear

# Probe cache state:
curl -X POST http://localhost:8080/api/cache/probe

6.3 DFlash Attention Engine

The DFlashEngine provides an alternative attention implementation for supported model architectures. To configure maximum context length:

DFLASH_MAX_CTX=131072 omlx serve

Note

DFlash requires specific model architectures. See known issues: models with 58 unmatched parameters will fail to load (issue #886). Verify architecture compatibility before enabling DFlash for new models.

6.4 SpecPrefill (Context Pruning)

omlx/patches/specprefill.py implements attention-score-based context pruning: low-attention tokens are dropped during prefill to reduce KV cache pressure for long contexts. This is a runtime monkey-patch applied transparently.

6.5 oQ Quantization

oMLX includes an on-device quantization pipeline (omlx/oq.py) for reducing model memory footprint:

# Check eligible models:
curl http://localhost:8080/api/oq/models

# Estimate quantization time:
curl "http://localhost:8080/api/oq/estimate?model_id=MODEL_ID&level=4"

# Start quantization:
curl -X POST http://localhost:8080/api/oq/start \
  -d '{"model_id": "MODEL_ID", "level": 4}'

# Monitor tasks:
curl http://localhost:8080/api/oq/tasks

Quantization levels are integer-valued (lower = more compression, higher = better quality). Calibration data is used to preserve accuracy at lower bit widths.

Warning

Quantizing a model while it is loaded in an active engine is not supported. Unload the model before starting oQ. Issue #913 documents that oQ on Qwen3.6 27B can break model weights — verify outputs against the unquantized model after any quantization pass.

6.6 TurboQuant KV Cache Quantization

omlx/turboquant_kv.py quantizes the KV cache itself (int8 or int4) to reduce GPU memory consumption during inference. Configured via the omlx/patches/turboquant_attention.py monkey-patch applied at startup.

6.7 Performance Benchmarking

# Start throughput benchmark via admin API:
curl -X POST http://localhost:8080/api/bench/start \
  -d '{"model_id": "MODEL_ID", "num_prompts": 100, "input_len": 256, "output_len": 64}'

# Stream results in real time:
curl http://localhost:8080/api/bench/BENCH_ID/stream

# Accuracy benchmarks (16 datasets):
curl -X POST http://localhost:8080/api/bench/accuracy/queue/add \
  -d '{"model_id": "MODEL_ID", "benchmarks": ["mmlu", "hellaswag", "gsm8k"]}'

curl http://localhost:8080/api/bench/accuracy/queue/status

Supported accuracy benchmarks: MMLU, MMLU-Pro, KMMLU, CMMLU, JMMLU, HellaSwag, TruthfulQA, ARC-Challenge, Winogrande, GSM8K, MathQA, HumanEval, MBPP, LiveCodeBench, BBQ, SafetyBench.


7. Security

7.1 Authentication Model

oMLX supports two authentication modes:

API Key authentication (inference endpoints):

# Pass via header:
curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"model": "...", "messages": [...]}'

# Configure:
OMLX_API_KEY=your-secret-key omlx serve

Session-based authentication (admin dashboard):

  • Login via POST /api/login with the API key
  • Session cookie issued; subsequent admin API calls use the session
  • Sub-keys allow delegated access with scoped permissions:
    curl -X POST http://localhost:8080/api/sub-keys \
      -d '{"name": "read-only-key", "permissions": ["read"]}'
    

Warning

If OMLX_API_KEY is not set, the inference endpoints accept unauthenticated requests. Always configure an API key before exposing oMLX on a network interface other than 127.0.0.1.

7.2 Secret Key

OMLX_SECRET_KEY signs admin session cookies. If not set, a random value is generated at startup (sessions do not survive restarts). For persistent sessions across restarts, set a fixed secret:

# Generate a strong secret:
python3 -c "import secrets; print(secrets.token_hex(32))"

# Set in settings.json under server.secret_key, or:
OMLX_SECRET_KEY=<generated-value> omlx serve

Caution

Never commit or log the OMLX_SECRET_KEY or OMLX_API_KEY values. Rotate them by stopping the server, updating the configuration, and restarting. Existing sessions are invalidated on key rotation.

7.3 Trust Remote Code

By default OMLX_TRUST_REMOTE_CODE=false. Setting this to true allows model config.json files to execute arbitrary Python code during model loading (via the transformers/mlx-lm trust_remote_code path).

Only enable this for models you own or have fully audited. Malicious models can exploit this to execute code with the server process's privileges.

7.4 Network Exposure

The default bind address 127.0.0.1 limits access to localhost. To expose on a network interface:

omlx serve --host 0.0.0.0 --port 8080 --api-key "strong-secret"

For internet-facing deployments, place oMLX behind a reverse proxy that handles TLS. Do not expose the admin dashboard (/admin/*, /api/*) to untrusted networks.

7.5 Proxy and TLS Configuration

For environments requiring traffic routing through corporate proxies or custom CA certificates:

OMLX_HTTPS_PROXY=http://proxy.corp.example.com:8080 \
OMLX_CA_BUNDLE=/etc/ssl/certs/corp-ca.pem \
OMLX_NO_PROXY=localhost,127.0.0.1,internal.example.com \
omlx serve

8. Developer Internals

8.1 Exception Hierarchy

omlx/exceptions.py defines a rich exception hierarchy. All oMLX exceptions descend from OMLXError:

OMLXError
├── CacheError
│   ├── CacheEvictionError
│   ├── CacheCorruptionError
│   └── SSDCacheError
├── SchedulerError
│   ├── SchedulerCapacityError
│   └── SchedulerStateError
├── ModelError
│   ├── ModelLoadError
│   ├── ModelNotFoundError
│   ├── ModelArchitectureError
│   └── ModelMemoryError
├── APIError
│   ├── AuthenticationError
│   ├── RateLimitError
│   └── ValidationError
├── EngineError
│   ├── EngineInitError
│   └── EngineExecutionError
└── ConfigError

Catch OMLXError to handle any oMLX-originating exception. Prefer specific subclasses in code that can meaningfully distinguish failure modes.

8.2 Engine Core — Single-Thread GPU Model

The EngineCore (omlx/engine_core.py) is the critical concurrency bottleneck:

# Conceptual structure (simplified)
class EngineCore:
    def __init__(self):
        self._executor = ThreadPoolExecutor(max_workers=1)  # SINGLE thread
        self._scheduler = Scheduler(...)

    async def step(self):
        # All GPU ops run on the single executor thread
        outputs = await asyncio.get_event_loop().run_in_executor(
            self._executor,
            self._scheduler.step  # MLX/Metal calls happen here
        )
        # Distribute outputs to waiting request collectors
        for output in outputs:
            self._output_collectors[output.request_id].put(output)

The OutputCollector (omlx/output_collector.py) implements a low-latency per-request buffer following the vLLM pattern. The asyncio event loop awaits GPU results without blocking; token streaming to clients happens from the output collector as each token is produced.

8.3 Scheduler Design

omlx/scheduler.py wraps mlx-lm's BatchGenerator and implements:

  • Prefill phase: processes new request prompts in batch
  • Decode phase: autoregressively generates tokens for in-flight requests
  • KV allocation: reserves blocks from PagedCacheManager before prefill
  • Preemption: evicts low-priority requests to SSD cache under memory pressure

The scheduler does not implement speculative decoding at the scheduler level; that is handled by the SpecPrefill patch at the attention layer.

8.4 Runtime Monkey-Patches

Patches in omlx/patches/ are applied at import time or during engine initialization:

Patch Module What It Fixes
gated_delta_advance.py qwen3_5 models GatedDeltaNet model missing cache.advance() method
index_cache.py Cache layer Index cache read optimization
specprefill.py Attention layer Attention-score-based context pruning
turboquant_attention.py Attention layer Injects int8/int4 KV quantization

Patches modify MLX or mlx-lm internals at runtime without forking the upstream libraries. When upgrading mlx-lm or mlx-vlm, verify that patched interfaces have not changed.

8.5 API Adapters

The omlx/api/adapters/ layer transforms between wire formats:

  • SSE Formatter: converts token output to text/event-stream chunks in OpenAI format
  • OpenAI Adapter: normalizes internal RequestOutput to OpenAI ChatCompletionChunk
  • Anthropic Adapter: converts internal format to Anthropic MessageStreamEvent
  • Base Adapter: abstract interface for new format additions

Tool call parsing (omlx/api/tool_calling.py) handles three model-specific formats:

  • Qwen format: JSON function calls in assistant messages
  • Llama format: <tool_call> XML-ish tags
  • Gemma format: Structured function call tokens

Thinking/reasoning extraction (omlx/api/thinking.py) strips <think>...</think> blocks from model output and surfaces them separately in the API response for reasoning-capable models (e.g., Qwen3, DeepSeek-R1 variants).

8.6 Structured Output (Grammar)

Structured output is implemented via xgrammar (omlx/api/grammar.py). This feature is optional and requires the --with-grammar Homebrew flag or manual xgrammar installation:

# Request JSON schema-constrained output:
curl http://localhost:8080/v1/chat/completions \
  -d '{
    "model": "...",
    "messages": [...],
    "response_format": {
      "type": "json_schema",
      "json_schema": {"name": "result", "schema": {"type": "object", ...}}
    }
  }'

# Check active grammar parsers:
curl http://localhost:8080/api/grammar/parsers

8.7 Module Reference — Core Modules

omlx/settings.py — GlobalSettings

Singleton backed by ~/.omlx/settings.json. Sections: server, model, memory, cache, auth, huggingface, modelscope, network, mcp, logging. Settings are loaded once at startup; runtime updates via POST /api/global-settings write through to the JSON file and update the in-process singleton.

omlx/config.py — Typed Configuration

Dataclass hierarchy: OMLXConfig aggregates ServerConfig, ModelConfig, GenerationConfig, SchedulerConfig, PagedSSDCacheConfig, MCPConfig. Used internally; not directly user-facing.

omlx/engine_pool.py — Engine Pool

Maintains a registry of loaded engines keyed by model ID. On get_or_load(model_id):

  1. If loaded and not stale: return existing engine
  2. If memory insufficient: LRU evict the least recently used engine
  3. Load model from disk, instantiate correct engine type, register

omlx/model_discovery.py — Model Discovery

Recursively scans --model-dir for directories meeting the three-file criterion (config.json + tokenizer + weights). Results are cached with mtime-based invalidation. Refresh via POST /api/presets/refresh.

omlx/memory_monitor.py — Memory Pressure Detection

Polls unified memory pressure at configurable intervals. Emits MemoryPressureEvent to registered listeners (engine pool, scheduler) when pressure crosses thresholds. Drives proactive LRU eviction before hard OOM.

omlx/logging_config.py — Structured Logging

Configures the root logger with:

  • TimedRotatingFileHandler (daily rotation, configurable retention)
  • StreamHandler with colored output (level-colored, for TTY)
  • Request context injection via contextvars (request_id, model_id appear in every log line within a request scope)

omlx/server_metrics.py — Metrics Collection

Collects per-model statistics: request count, token throughput (input/output tokens/sec), latency percentiles (p50/p95/p99), cache hit rates. Persisted in memory; reset via admin API. Surfaced at GET /api/stats.

8.8 Admin Dashboard Frontend

The admin dashboard (omlx/admin/templates/) uses:

  • Jinja2 templating (server-side rendered HTML shell)
  • Tailwind CSS for styling
  • Alpine.js for reactive UI state
  • Lucide icons for iconography
  • highlight.js for code syntax highlighting in chat view
  • KaTeX for mathematical formula rendering
  • marked.js for Markdown rendering in chat

Pages: dashboard (overview), chat (interactive model playground), login, settings, models (management), benchmarks, logs.

8.9 Testing

Tests live in tests/. The pytest suite uses custom markers:

# Run all tests:
pytest tests/

# Run only fast unit tests (exclude slow and integration):
pytest tests/ -m "not slow and not integration"

# Run integration tests (requires running server):
pytest tests/ -m integration

# Run slow tests (long-running benchmarks/accuracy evals):
pytest tests/ -m slow

Pytest markers:

  • slow — long-running tests (benchmark execution, large model loading)
  • integration — require a live oMLX server; use --server-url fixture

Test structure:

tests/
├── unit/           # Pure unit tests, no server required
├── integration/    # Require live server (marked integration)
├── benchmarks/     # Performance regression tests (marked slow)
└── conftest.py     # Shared fixtures (server_url, api_client, etc.)

8.10 Contributing

Based on standard conventions in the repository:

Development setup:

git clone https://github.com/jundot/omlx
cd omlx
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Verify environment:
omlx diagnose

Code standards:

  • Python type hints on all public interfaces
  • Docstrings for all public classes and functions
  • Tests for new engine types and cache variants
  • Patches in omlx/patches/ must include a comment explaining what upstream bug/limitation they address and when they can be removed

Pull request checklist:

  • pytest tests/ -m "not slow and not integration" passes locally
  • New environment variables added to the settings.py mapping and this guide
  • Monkey-patches documented in omlx/patches/ module docstring
  • New API endpoints added to omlx/admin/routes.py router inventory

9. Integration Points

9.1 OpenAI SDK Compatibility

oMLX is a drop-in replacement for the OpenAI API for chat, completions, and embeddings:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8080/v1",
    api_key="your-omlx-api-key"
)

response = client.chat.completions.create(
    model="mlx-community/Llama-3.2-3B-Instruct-4bit",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True
)

9.2 Anthropic SDK Compatibility

The /v1/messages and /v1/messages/count_tokens endpoints implement the Anthropic Messages API:

import anthropic

client = anthropic.Anthropic(
    base_url="http://localhost:8080",
    api_key="your-omlx-api-key"
)

response = client.messages.create(
    model="mlx-community/Llama-3.2-3B-Instruct-4bit",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}]
)

9.3 Codex / OpenCode Compatibility

The /v1/responses endpoint implements the OpenAI Responses API for Codex and OpenCode compatibility. Responses are stored in-memory and retrievable by ID. The omlx/integrations/ directory contains additional integration shims for specific tools.

9.4 Model Context Protocol (MCP)

oMLX acts as an MCP client, connecting to external tool servers and exposing their tools to inference requests. The MCPClientManager maintains persistent connections to all configured servers.

Using MCP tools in inference:

{
  "model": "mlx-community/Qwen2.5-7B-Instruct-4bit",
  "messages": [...],
  "tools": "auto"
}

When tools: "auto" is specified, oMLX injects available MCP tools into the context. Tool calls in model output are routed through ToolExecutor to the appropriate MCP server.

Direct MCP tool execution:

# List available tools:
curl http://localhost:8080/tools

# List connected servers:
curl http://localhost:8080/servers

# Execute a tool directly:
curl -X POST http://localhost:8080/execute \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"tool_name": "read_file", "arguments": {"path": "/tmp/test.txt"}}'

9.5 HuggingFace Ecosystem

oMLX integrates with the HuggingFace Hub for model discovery, downloading, and uploading:

# Configure HF token (required for gated models):
# In settings.json: huggingface.token = "hf_..."
# Or via admin settings UI

# Download a model:
curl -X POST http://localhost:8080/api/hf/download \
  -d '{"model_id": "mlx-community/Llama-3.2-3B-Instruct-4bit"}'

# Monitor download progress:
curl http://localhost:8080/api/hf/tasks

# Upload a quantized model:
curl -X POST http://localhost:8080/api/upload/start \
  -d '{"model_id": "local-model-name", "repo_id": "your-org/model-name"}'

For users in China or with restricted Hub access, override the HuggingFace endpoint:

OMLX_HF_ENDPOINT=https://hf-mirror.com omlx serve

9.6 ModelScope Integration

ModelScope (Chinese ML model registry) is a first-class integration:

# Configure ModelScope token in settings.json:
# modelscope.token = "your-token"
# modelscope.endpoint = "https://modelscope.cn"  (or override)

# Download from ModelScope:
curl -X POST http://localhost:8080/api/ms/download \
  -d '{"model_id": "qwen/Qwen2.5-7B-Instruct"}'

Troubleshooting

Error: DFlash model fails to load: 58 parameters not matching architecture

Cause: The model's weight tensors do not match the DFlash engine's expected architecture. Typically affects models with non-standard layer configurations (issue #886).

Solution: Switch the model to the standard BatchedEngine by removing any explicit DFlash configuration for this model. If DFlash is required, check that the model was exported in a DFlash-compatible format.

Prevention: Run omlx diagnose or attempt a load via POST /api/models/{id}/load before putting a model into production use. Verify architecture compatibility in the model's config.json before enabling DFlash.


Error: SpecPrefill system_end calc failed

Cause: The SpecPrefill context pruning patch encountered an invalid boundary calculation, usually from an unexpected prompt structure or token sequence (issue #916).

Solution: The request will fall back to full-context prefill automatically. If this error is frequent, disable SpecPrefill by ensuring the specprefill.py patch is not applied, or open an issue with the prompt that triggers it.

Prevention: Test prompts with unusual structures (e.g., empty system messages, unusual special tokens) against the SpecPrefill patch on upgrade.


Error: RuntimeError: no Stream(gpu, 0) in current thread

Cause: An MLX GPU operation was called outside the designated single executor thread (issue #904). This can occur on M4 Max or when third-party code calls MLX directly.

Solution: Ensure all MLX compute calls go through EngineCore's executor thread. Do not call MLX operations from asyncio coroutines, background threads, or signal handlers directly.

Prevention: Custom engine extensions must use run_in_executor with the engine's _executor to schedule any MLX calls. Never call mx.eval() or similar outside the executor thread.


Error: ToolCallStreamFilter crashes on streaming tool calls

Cause: The tool call stream parser encountered malformed JSON in a partial streaming chunk (issue #882). Typically affects models that split JSON tool call tokens across multiple SSE events.

Solution: For affected models, disable streaming for tool-call-heavy workflows ("stream": false in the request). Upgrade to the latest patch version, as this was fixed post-#882.

Prevention: When adding new tool-calling models, test streaming with multi-step tool calls before deploying.


Error: Slow generation speed on 0.3.7 (or current version)

Cause: Regression in continuous batching throughput (issues #937/#936). May also relate to SSD cache write pressure or prefix cache miss rates.

Solution:

  1. Check GET /api/stats for cache hit rates and token throughput.
  2. Try --hot-cache-only true to isolate SSD write overhead.
  3. Reduce OMLX_MAX_CONCURRENT_REQUESTS to decrease scheduler overhead.
  4. Clear stats (POST /api/stats/clear) and re-benchmark after each change.

Prevention: Run throughput benchmarks via POST /api/bench/start after every upgrade before moving to production.


Error: Infinite loop with DFlash + certain models

Cause: DFlash attention engine enters an infinite decode loop with specific model+prompt combinations (issues #911/#934/#935). Root cause is an edge case in DFlash's attention mask handling.

Solution: Unload the model (POST /api/models/{id}/unload), restart the server if the loop consumes all memory, and switch to the standard BatchedEngine for the affected model.

Warning

An infinite loop in the DFlash engine will exhaust unified memory and cause the macOS kernel to terminate the process under memory pressure. Monitor memory via GET /api/device-info when testing new models with DFlash.

Prevention: Test new models with DFlash on short inputs first. Add the model to an explicit blocklist in model settings if DFlash loops are observed.


Error: oQ on Qwen3.6 27b breaks model

Cause: The oQ quantization calibration pass for Qwen3.6 27B modifies weight tensors in a way that corrupts output (issue #913). This may affect other large models with similar architectures.

Solution: Restore from the original (pre-quantization) model files. Always back up the original model directory before running oQ.

Prevention: Before any oQ quantization:

  1. Copy the model directory to a backup location.
  2. Run a short accuracy benchmark (POST /api/bench/accuracy/queue/add) on the original.
  3. Run oQ, then repeat the benchmark and compare scores.
  4. If accuracy drops significantly, restore from backup.

Note

The DELETE /api/hf/models/{name} endpoint permanently deletes local model files. Confirm you have either the original on HuggingFace or a local backup before deleting a quantized model you wish to revert.


Quick Reference — Common curl Commands

# Health check
curl http://localhost:8080/health

# List models
curl http://localhost:8080/v1/models

# Chat completion
curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"Hello"}]}'

# Streaming chat
curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer KEY" \
  -d '{"model":"MODEL_ID","messages":[...],"stream":true}'

# Embeddings
curl http://localhost:8080/v1/embeddings \
  -H "Authorization: Bearer KEY" \
  -d '{"model":"EMBED_MODEL_ID","input":"text to embed"}'

# Unload model
curl -X POST http://localhost:8080/v1/models/MODEL_ID/unload \
  -H "Authorization: Bearer KEY"

# Stats
curl http://localhost:8080/api/stats

# Device info (memory)
curl http://localhost:8080/api/device-info