mlx-vlm-batch-outlines
mlx-vlm-batch-outlines is a small Qwen-focused MLX vision-language package for:
- multimodal chat with images
- batched multimodal chat
- constrained decoding with
llguidance - structured outputs from Pydantic, regex, CFG, or JSON Schema
- simple video inference helpers
It is intentionally narrow. It is built for local MLX/Qwen workflows and does not try to support every mlx-vlm backend or the full Outlines API surface.
Attribution
This project heavily reuses and adapts ideas and code paths from:
In particular:
- the MLX/Qwen multimodal runtime and model code are derived from
mlx-vlm - the constrained decoding architecture and structured-output interface are derived from
outlines
Scope
This package currently targets Qwen vision models exposed through MLX, such as:
mlx-community/Qwen3.5-4B-MLX-4bitmlx-community/Qwen2-VL-2B-Instruct-4bit
The public API is:
load(...)chat(...)chat_stream(...)batch_chat(...)video_chat(...)video_chunk_process(...)
Structured output helpers:
RegexCFGJsonSchemaregex(...)cfg(...)json_schema(...)
Install
uv sync
Or with plain pip:
pip install -e .
Quick Start
from PIL import Image
from mlx_vlm_batch_outlines import chat, load
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
image = Image.open("cat.jpeg")
result = chat(
model,
processor,
[
{"role": "system", "content": "Answer concisely."},
{"role": "user", "content": ["Describe this image.", image]},
],
max_tokens=80,
)
print(result.text)
Batched Image Chat
from PIL import Image
from mlx_vlm_batch_outlines import batch_chat, load
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
cat = Image.open("cat.jpeg")
dog = Image.open("dog.jpeg")
results = batch_chat(
model,
processor,
[
[{"role": "user", "content": ["Describe this image.", cat]}],
[{"role": "user", "content": ["Describe this image.", dog]}],
],
max_tokens=80,
)
for text in results.texts:
print(text)
Structured Outputs
Pydantic
from PIL import Image
from pydantic import BaseModel
from mlx_vlm_batch_outlines import chat, load
class VisualSummary(BaseModel):
primary_subject: list[str]
subject_count: int
setting: str
short_description: str
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
image = Image.open("cat.jpeg")
result = chat(
model,
processor,
[
{
"role": "user",
"content": ["Describe this image as JSON using the requested schema.", image],
}
],
output_type=VisualSummary,
max_tokens=140,
)
print(result.model_dump())
CFG
from PIL import Image
from mlx_vlm_batch_outlines import CFG, chat, load
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
image = Image.open("cat.jpeg")
result = chat(
model,
processor,
[
{
"role": "user",
"content": [
"What animal is most prominent in this image? Choose either cat or dog.",
image,
],
}
],
output_type=CFG('start: "cat" | "dog"'),
max_tokens=12,
)
print(result.text)
Batch + Pydantic
from PIL import Image
from pydantic import BaseModel
from mlx_vlm_batch_outlines import batch_chat, load
class VisualSummary(BaseModel):
primary_subject: list[str]
subject_count: int
setting: str
short_description: str
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
cat = Image.open("cat.jpeg")
dog = Image.open("dog.jpeg")
results = batch_chat(
model,
processor,
[
[{"role": "user", "content": ["Describe this image.", cat]}],
[{"role": "user", "content": ["Describe this image.", dog]}],
],
output_type=VisualSummary,
max_tokens=180,
)
for item in results:
print(item.model_dump())
Video Chat
video_chat(...) is the thin direct video path. It samples frames across the whole video and runs one multimodal generation call.
from mlx_vlm_batch_outlines import load, video_chat
model, processor = load("mlx-community/Qwen2-VL-2B-Instruct-4bit")
result = video_chat(
model,
processor,
video="path/to/video.mp4",
prompt="Describe this video.",
fps=1.0,
max_pixels=(224, 224),
max_tokens=100,
)
print(result.text)
Chunked Video Processing
video_chunk_process(...) treats a single video as many independent image-chat chunks.
For each chunk it:
- slices the video by time
- samples frames inside that chunk
- converts those frames into a normal multi-image chat
- runs chunk chats through
batch_chat(...)in mini-batches
This is useful when you want independent chunk summaries instead of one global video answer.
from pydantic import BaseModel
from mlx_vlm_batch_outlines import load, video_chunk_process
class ChunkSummary(BaseModel):
actions: list[str]
scene: str
model, processor = load("mlx-community/Qwen3.5-4B-MLX-4bit")
results = video_chunk_process(
model,
processor,
video="path/to/video.mp4",
prompt="Describe what is happening in this chunk.",
chunk_length_seconds=5.0,
batch_size=4,
fps=1.0,
max_frames_per_chunk=8,
output_type=ChunkSummary,
max_tokens=120,
)
for item in results:
print(item["chunk_index"], item["start_sec"], item["end_sec"], item["output"])
Each returned item looks like:
{
"chunk_index": 0,
"start_sec": 0.0,
"end_sec": 5.0,
"output": ...,
}
If output_type is structured, output is the parsed structured object. Otherwise it is the raw text string for that chunk.
Notes
- Default image resize is currently
224x224. - Smaller image sizes can improve batching throughput significantly because image token count drops quickly with resolution.
- Homogeneous batches usually perform better than mixed multimodal shapes.
batch_chat_stream(...)is not implemented.- This package is Qwen-focused and not intended as a generic VLM abstraction layer.
Benchmark Notes
These are local measurements from the development machine, not a formal benchmark suite.
-
4B Qwen, homogeneous
3 x 1-imagebatch,768x768:- sequential: about
19.25s - batch: about
15.63s - speedup: about
1.23x
- sequential: about
-
4B Qwen, homogeneous
3 x 1-imagebatch,384x384, short CFG output:- sequential: about
6.25s - batch: about
2.94s - speedup: about
2.13x
- sequential: about
The practical takeaway is simple:
- batching helps more when image sizes are smaller
- batching helps more when the workload is homogeneous
- image resolution matters a lot because image token count grows quickly with width and height
Local Verification
There is a simple verifier script in:
Run it with:
uv run python verify_mlx_vlm_batch_outlines.py
Metadata
Release files for mlx-vlm-batch-outlines 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| mlx_vlm_batch_outlines-0.1.0.tar.gz | 125.3 kB | Without provenance | Details for mlx_vlm_batch_outlines-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mlx_vlm_batch_outlines-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for mlx_vlm_batch_outlines-0.1.0-py3-none-any.whl |
Total release size: 259.9 kB
Release files / mlx_vlm_batch_outlines-0.1.0.tar.gz
| Download URL | mlx_vlm_batch_outlines-0.1.0.tar.gz |
|---|---|
| Size | 125.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0d5821b295d5f375a41f28c2a25700f4956d0b242b17d7e219d3efcbeeadd658
|
|
BLAKE2b-256 checksum How to use checksums |
e9015d1a38ae5f508d5bbdb210d176a33f343500ea119555e0474cdd883f7c97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mlx_vlm_batch_outlines-0.1.0-py3-none-any.whl
| Download URL | mlx_vlm_batch_outlines-0.1.0-py3-none-any.whl |
|---|---|
| Size | 134.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f0e1b15b5e259add7e47a94baba79f8b6c453bc0b49ceb05cd4da78ff067093
|
|
BLAKE2b-256 checksum How to use checksums |
d14ba30b7f1a5d0ad39766f560a77961f0fe849364bfd9072f015912ff470a16
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for mlx-vlm-batch-outlines 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.