---
title: mlx_server
---

<div class="python-reference">
<a id="fastvideo.entrypoints.openai.mlx_server-classes"></a>
<a id="fastvideo.entrypoints.openai.mlx_server-functions"></a>
<a id="fastvideo.entrypoints.openai.mlx_server"></a>
<p>Serve native FastH3 MLX through the shared video-job API and playground.</p>
<h2 id="fastvideo.entrypoints.openai.mlx_server.explicit_request_updates"><code>fastvideo.entrypoints.openai.mlx_server.explicit_request_updates</code></h2>
<pre class="doc-signature"><code>explicit_request_updates(request: GenerationRequest) -&gt; dict&#91;str, Any&#93;</code></pre>
<p>Project a <code>GenerationRequest</code> down to <em>explicitly set</em> fields only.</p>
<p>Returns a flat kwargs dict suitable for merging into a generator call.
The projection uses <code>_fastvideo_explicit_paths</code> (populated during
<code>parse_config</code> / raw binding) so schema defaults on the dataclass
are <strong>not</strong> emitted — only paths the caller/operator actually wrote.</p>
<p>This is what makes <code>ServeConfig.default_request</code> work as an
operator-pinned baseline rather than a full override: a YAML with just
<code>sampling.seed: 42</code> yields <code>&#123;"seed": 42&#125;</code>, not the full sampling
config with its 15 schema defaults.</p>
<p>Precondition: the request must carry <code>_fastvideo_explicit_paths</code> —
populated by :func:<code>fastvideo.api.parser.parse_config</code> or
:func:<code>fastvideo.api.compat.normalize_generation_request</code>. Calling on
a raw <code>GenerationRequest()</code> asserts.</p>
<h2 id="fastvideo.entrypoints.openai.mlx_server.normalize_generation_request"><code>fastvideo.entrypoints.openai.mlx_server.normalize_generation_request</code></h2>
<pre class="doc-signature"><code>normalize_generation_request(request: GenerationRequest | Mapping&#91;str, Any&#93;) -&gt; GenerationRequest</code></pre>

<h2 id="fastvideo.entrypoints.openai.mlx_server.GenerationRequest"><code>fastvideo.entrypoints.openai.mlx_server.GenerationRequest</code></h2>
<pre class="doc-signature"><code>GenerationRequest(prompt: str | list&#91;str&#93; | None = None, negative_prompt: str | None = None, inputs: InputConfig = InputConfig(), sampling: SamplingConfig = SamplingConfig(), runtime: RequestRuntimeConfig = RequestRuntimeConfig(), output: OutputConfig = OutputConfig(), stage_overrides: dict&#91;str, Any&#93; = dict(), state: ContinuationState | None = None, plan: GenerationPlan | None = None, extensions: dict&#91;str, Any&#93; = dict())</code></pre>


<h2 id="fastvideo.entrypoints.openai.mlx_server.create_app"><code>fastvideo.entrypoints.openai.mlx_server.create_app</code></h2>
<pre class="doc-signature"><code>create_app(fastvideo_args: FastVideoArgs, output_dir: str = DEFAULT_OUTPUT_DIR, default_request: GenerationRequest | None = None, served_model_name: str | None = None, *, generator_factory: Callable&#91;&#91;&#93;, ServingGenerator&#93; | None = None, video_request_validator: Callable&#91;&#91;VideoGenerationRequest&#93;, None&#93; | None = None, runtime: str = &#x27;cuda&#x27;, media_reference_max_bytes: int = DEFAULT_MEDIA_REFERENCE_MAX_BYTES, media_policy: MediaPolicy | None = None) -&gt; FastAPI</code></pre>
<p>Build the FastAPI application with all routers mounted</p>
<h2 id="fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest"><code>fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest</code></h2>
<pre class="doc-signature"><code>VideoGenerationRequest()</code></pre>
<p>OpenAI/vLLM-Omni-compatible video generation request.</p>
<p>Model-specific parameters belong in <code>extra_params</code>. FastVideo keeps the
legacy <code>input_reference</code> and <code>reference_url</code> fields for clients that
predate vLLM-Omni's typed reference objects.</p>
<h3 id="fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest.validate_prompt"><code>fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest.validate_prompt</code></h3>
<pre class="doc-signature"><code>validate_prompt(value: str) -&gt; str</code></pre>

<h3 id="fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest.resolve_video_params"><code>fastvideo.entrypoints.openai.mlx_server.VideoGenerationRequest.resolve_video_params</code></h3>
<pre class="doc-signature"><code>resolve_video_params() -&gt; VideoParams</code></pre>
<p>Resolve top-level, nested, and <code>size</code> dimensions like vLLM-Omni.</p>
<h2 id="fastvideo.entrypoints.openai.mlx_server.mlx_http_steps"><code>fastvideo.entrypoints.openai.mlx_server.mlx_http_steps</code></h2>
<pre class="doc-signature"><code>mlx_http_steps(model_path: str) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def mlx_http_steps(model_path: str) -&gt; int:
    return 9 if model_path == EIGHT_STEP_MODEL else 5</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.mlx_num_steps"><code>fastvideo.entrypoints.openai.mlx_server.mlx_num_steps</code></h2>
<pre class="doc-signature"><code>mlx_num_steps(num_inference_steps: int | None, *, model_path: str) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def mlx_num_steps(num_inference_steps: int | None, *, model_path: str) -&gt; int:
    expected = mlx_http_steps(model_path)
    if num_inference_steps not in (None, expected):
        raise ValueError(f&quot;This FastH3 MLX server uses &#123;expected&#125; sigma points &quot;
                         f&quot;(&#123;HTTP_STEPS_TO_FORWARDS&#91;expected&#93;&#125; transformer forwards).&quot;)
    return HTTP_STEPS_TO_FORWARDS&#91;expected&#93;</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.MLXGeneratorConfig"><code>fastvideo.entrypoints.openai.mlx_server.MLXGeneratorConfig</code></h2>
<pre class="doc-signature"><code>MLXGeneratorConfig()</code></pre>

<details><summary>Source</summary><pre><code>class MLXGeneratorConfig(BaseModel):
    model_config = ConfigDict(extra=&quot;forbid&quot;)
    model_path: Literal&#91;&quot;FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2&quot;,
                        &quot;FastVideo/FastVideo-FastH3-8-Step-V2&quot;&#93; = PREVIEW_MODEL
    model_root: str
    mlx_checkpoint: str
    prompt_cache_dir: str = &quot;outputs/h3_prompt_cache&quot;
    vae_dtype: Literal&#91;&quot;fp32&quot;, &quot;fp16&quot;, &quot;bf16&quot;&#93; = &quot;fp32&quot;
    vsa: bool = False
    vsa_sparsity: float = Field(default=0.9, ge=0.0, lt=1.0)
    vsa_tile_size: Literal&#91;64, 256&#93; = 64</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.MLXServerConfig"><code>fastvideo.entrypoints.openai.mlx_server.MLXServerConfig</code></h2>
<pre class="doc-signature"><code>MLXServerConfig()</code></pre>

<details><summary>Source</summary><pre><code>class MLXServerConfig(BaseModel):
    model_config = ConfigDict(extra=&quot;forbid&quot;)
    host: str = &quot;127.0.0.1&quot;
    port: int = Field(default=8000, ge=1, le=65535)
    output_dir: str = &quot;outputs/mlx_fasth3&quot;
    served_model_name: str = Field(default=&quot;fasth3&quot;, min_length=1)</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.MLXServeConfig"><code>fastvideo.entrypoints.openai.mlx_server.MLXServeConfig</code></h2>
<pre class="doc-signature"><code>MLXServeConfig()</code></pre>

<details><summary>Source</summary><pre><code>class MLXServeConfig(BaseModel):
    model_config = ConfigDict(extra=&quot;forbid&quot;)
    runtime: Literal&#91;&quot;mlx&quot;&#93;
    generator: MLXGeneratorConfig
    server: MLXServerConfig = Field(default_factory=MLXServerConfig)
    default_request: dict&#91;str, Any&#93;</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.validate_mlx_video_request"><code>fastvideo.entrypoints.openai.mlx_server.validate_mlx_video_request</code></h2>
<pre class="doc-signature"><code>validate_mlx_video_request(request: VideoGenerationRequest, *, model_path: str = PREVIEW_MODEL) -&gt; None</code></pre>
<p>Reject unsupported inputs before fetching media or creating a job.</p>
<details><summary>Source</summary><pre><code>def validate_mlx_video_request(request: VideoGenerationRequest, *, model_path: str = PREVIEW_MODEL) -&gt; None:
    &quot;&quot;&quot;Reject unsupported inputs before fetching media or creating a job.&quot;&quot;&quot;
    allowed = &#123;
        &quot;model&quot;,
        &quot;prompt&quot;,
        &quot;seed&quot;,
        &quot;size&quot;,
        &quot;width&quot;,
        &quot;height&quot;,
        &quot;fps&quot;,
        &quot;num_frames&quot;,
        &quot;seconds&quot;,
        &quot;video_params&quot;,
        &quot;task&quot;,
        &quot;guidance_scale&quot;,
        &quot;num_inference_steps&quot;,
        &quot;negative_prompt&quot;,
    &#125;
    unsupported = request.model_fields_set - allowed
    if unsupported:
        raise ValueError(&quot;H3 MLX serving does not support: &quot; + &quot;, &quot;.join(sorted(unsupported)))
    if request.task not in (None, &quot;t2va&quot;):
        raise ValueError(&quot;H3 MLX serving supports task=t2va only.&quot;)
    if request.guidance_scale not in (None, 1.0):
        raise ValueError(&quot;FastH3 MLX requires guidance_scale=1.&quot;)
    if request.negative_prompt not in (None, &quot;&quot;):
        raise ValueError(&quot;FastH3 MLX does not use a negative prompt.&quot;)
    expected = mlx_http_steps(model_path)
    if request.num_inference_steps not in (None, expected):
        raise ValueError(f&quot;This FastH3 MLX server uses &#123;expected&#125; sigma points &quot;
                         f&quot;(&#123;HTTP_STEPS_TO_FORWARDS&#91;expected&#93;&#125; transformer forwards).&quot;)
    if request.seed is not None and not 0 &lt;= request.seed &lt;= 2**32 - 1:
        raise ValueError(&quot;H3 MLX seed must be between 0 and 4294967295.&quot;)</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.MLXH3Generator"><code>fastvideo.entrypoints.openai.mlx_server.MLXH3Generator</code></h2>
<pre class="doc-signature"><code>MLXH3Generator(config: MLXGeneratorConfig)</code></pre>
<p>Keep one pipeline on one MLX thread; preserve its phase-memory policy.</p>

<details><summary>Source</summary><pre><code>class MLXH3Generator:
    &quot;&quot;&quot;Keep one pipeline on one MLX thread; preserve its phase-memory policy.&quot;&quot;&quot;

    def __init__(self, config: MLXGeneratorConfig) -&gt; None:
        self._model_path = config.model_path
        self._vsa = config.vsa
        self._vsa_sparsity = config.vsa_sparsity
        self._vsa_tile_size = config.vsa_tile_size
        self._worker = ThreadPoolExecutor(max_workers=1, thread_name_prefix=&quot;h3-mlx&quot;)
        try:
            self._pipeline = self._worker.submit(self._load, config).result()
        except BaseException:
            self._worker.shutdown(wait=True)
            raise

    @staticmethod
    def _load(config: MLXGeneratorConfig):
        if platform.system() != &quot;Darwin&quot; or platform.machine() != &quot;arm64&quot;:
            raise RuntimeError(&quot;H3 MLX serving requires an Apple Silicon Mac.&quot;)
        if shutil.which(&quot;ffmpeg&quot;) is None:
            raise RuntimeError(&quot;Install ffmpeg before starting the H3 MLX server.&quot;)
        from fastvideo.mlx_runtime.minimax_h3_pipeline import MiniMaxH3MLXPipeline

        return MiniMaxH3MLXPipeline(
            model_root=Path(config.model_root).expanduser(),
            mlx_dit_checkpoint=Path(config.mlx_checkpoint).expanduser(),
            prompt_cache_dir=Path(config.prompt_cache_dir).expanduser(),
            vae_dtype=config.vae_dtype,
        )

    def generate(self, request: GenerationRequest) -&gt; dict&#91;str, Any&#93;:
        return self._worker.submit(self._generate, request).result()

    def _generate(self, request: GenerationRequest) -&gt; dict&#91;str, Any&#93;:
        started = time.perf_counter()
        generate_kwargs: dict&#91;str, Any&#93; = &#123;
            &quot;output_path&quot;: request.output.output_path,
            &quot;width&quot;: request.sampling.width,
            &quot;height&quot;: request.sampling.height,
            &quot;num_frames&quot;: request.sampling.num_frames,
            &quot;seed&quot;: request.sampling.seed,
            &quot;num_steps&quot;: mlx_num_steps(request.sampling.num_inference_steps, model_path=self._model_path),
        &#125;
        if self._vsa:
            generate_kwargs.update(
                vsa=True,
                vsa_sparsity=self._vsa_sparsity,
                vsa_tile_size=self._vsa_tile_size,
            )
        result = self._pipeline.generate(request.prompt, **generate_kwargs)
        # Do not retain decoded frames/waveforms or label phase peaks as total RAM.
        return &#123;&quot;video_path&quot;: str(result.video_path), &quot;generation_time&quot;: time.perf_counter() - started&#125;

    def shutdown(self) -&gt; None:

        def release():
            self._pipeline = None
            from fastvideo.mlx_runtime.minimax_h3_pipeline import _cleanup_mlx

            _cleanup_mlx()

        try:
            self._worker.submit(release).result()
        finally:
            self._worker.shutdown(wait=True)</code></pre></details>
<h3 id="fastvideo.entrypoints.openai.mlx_server.MLXH3Generator.generate"><code>fastvideo.entrypoints.openai.mlx_server.MLXH3Generator.generate</code></h3>
<pre class="doc-signature"><code>generate(request: GenerationRequest) -&gt; dict&#91;str, Any&#93;</code></pre>

<details><summary>Source</summary><pre><code>def generate(self, request: GenerationRequest) -&gt; dict&#91;str, Any&#93;:
    return self._worker.submit(self._generate, request).result()</code></pre></details>
<h3 id="fastvideo.entrypoints.openai.mlx_server.MLXH3Generator.shutdown"><code>fastvideo.entrypoints.openai.mlx_server.MLXH3Generator.shutdown</code></h3>
<pre class="doc-signature"><code>shutdown() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def shutdown(self) -&gt; None:

    def release():
        self._pipeline = None
        from fastvideo.mlx_runtime.minimax_h3_pipeline import _cleanup_mlx

        _cleanup_mlx()

    try:
        self._worker.submit(release).result()
    finally:
        self._worker.shutdown(wait=True)</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.load_config"><code>fastvideo.entrypoints.openai.mlx_server.load_config</code></h2>
<pre class="doc-signature"><code>load_config(path: str) -&gt; MLXServeConfig</code></pre>

<details><summary>Source</summary><pre><code>def load_config(path: str) -&gt; MLXServeConfig:
    with open(path, encoding=&quot;utf-8&quot;) as source:
        return MLXServeConfig.model_validate(yaml.safe_load(source))</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.create_mlx_app"><code>fastvideo.entrypoints.openai.mlx_server.create_mlx_app</code></h2>
<pre class="doc-signature"><code>create_mlx_app(config: MLXServeConfig)</code></pre>

<details><summary>Source</summary><pre><code>def create_mlx_app(config: MLXServeConfig):
    request = normalize_generation_request(config.default_request)
    explicit = explicit_request_updates(request)
    supported = &#123;
        &quot;width&quot;, &quot;height&quot;, &quot;num_frames&quot;, &quot;fps&quot;, &quot;seed&quot;, &quot;num_inference_steps&quot;, &quot;guidance_scale&quot;, &quot;negative_prompt&quot;
    &#125;
    if set(explicit) - supported:
        raise ValueError(&quot;MLX default_request contains unsupported fields: &quot; +
                         &quot;, &quot;.join(sorted(set(explicit) - supported)))
    required = &#123;&quot;width&quot;, &quot;height&quot;, &quot;num_frames&quot;, &quot;fps&quot;, &quot;seed&quot;, &quot;num_inference_steps&quot;, &quot;guidance_scale&quot;&#125;
    if required - set(explicit):
        raise ValueError(&quot;MLX default_request must set: &quot; + &quot;, &quot;.join(sorted(required - set(explicit))))
    validate_mlx_video_request(VideoGenerationRequest(prompt=&quot;validate config&quot;, **explicit),
                               model_path=config.generator.model_path)
    # Transport admission uses the registered H3 family, not CUDA engine options.
    args = SimpleNamespace(model_path=config.generator.model_path,
                           lora_path=None,
                           lora_nickname=&quot;default&quot;,
                           lora_strength=1.0,
                           override_pipeline_cls_name=None)
    from fastvideo.entrypoints.openai.request_adapter import build_generation_request

    build_generation_request(&quot;config-check&quot;,
                             VideoGenerationRequest(prompt=&quot;validate config&quot;),
                             args,
                             served_model_name=config.server.served_model_name,
                             output_dir=config.server.output_dir,
                             default_request=request)

    def video_request_validator(request: VideoGenerationRequest) -&gt; None:
        validate_mlx_video_request(request, model_path=config.generator.model_path)

    return create_app(
        args,
        config.server.output_dir,
        request,
        config.server.served_model_name,
        generator_factory=lambda: MLXH3Generator(config.generator),
        video_request_validator=video_request_validator,
        runtime=&quot;mlx&quot;,
    )</code></pre></details>
<h2 id="fastvideo.entrypoints.openai.mlx_server.main"><code>fastvideo.entrypoints.openai.mlx_server.main</code></h2>
<pre class="doc-signature"><code>main() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def main() -&gt; None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(&quot;--config&quot;,
                        required=True,
                        help=&quot;H3 MLX serving YAML; paths are relative to the working directory&quot;)
    args = parser.parse_args()
    config = load_config(args.config)
    uvicorn.run(create_mlx_app(config), host=config.server.host, port=config.server.port)</code></pre></details>
</div>
