1- # Contributing to mlx-manager
1+ # Contributing to mlx-serve
2+
3+ Thank you for your interest in contributing to mlx-serve! This document covers everything you need to get started.
24
35## Prerequisites
46
79- Python 3.11+
810- [ uv] ( https://docs.astral.sh/uv/ ) — ` curl -LsSf https://astral.sh/uv/install.sh | sh `
911
10- ## Setup
12+ ## Getting Started
1113
1214``` bash
13- git clone https://github.com/your-username/mlx-manager
14- cd mlx-manager
15+ # 1. Fork and clone
16+ git clone https://github.com/YOUR-USERNAME/mlx-serve.git
17+ cd mlx-serve
18+
19+ # 2. Install all dependencies (including dev + all model backends)
1520make install
21+
22+ # 3. Verify everything works
23+ make test
24+ make lint
1625```
1726
18- ## Running locally
27+ ## Project Layout
1928
20- ``` bash
21- make mlx-start # start on port 8095
22- make status # check state
23- make mlx-stop # stop everything
29+ ```
30+ src/mlx_serve/
31+ ├── __init__.py Version declaration
32+ ├── cli.py Command-line interface (init, start, stop, status, logs)
33+ ├── config.py YAML config loading, typed constants
34+ ├── router.py OpenAI-compatible HTTP endpoints
35+ ├── process_manager.py Subprocess lifecycle (text/vision models)
36+ ├── inline_manager.py In-process lifecycle (embedding/TTS/STT models)
37+ ├── events.py Structured lifecycle event log
38+ ├── metrics.py Request metrics and memory monitoring
39+ ├── logging_config.py Logging setup
40+ ├── main.py FastAPI app and lifespan
41+ └── _default_models.yaml Bundled fallback config
2442```
2543
26- Dev mode (auto-reloads on code changes):
44+ Key constraint: ` process_manager ` and ` inline_manager ` must not import each other. All cross-manager coordination belongs in ` router.py ` .
45+
46+ ## Development Workflow
47+
48+ ### Running locally
2749
2850``` bash
29- make mlx-dev
51+ make dev # start with auto-reload on code changes
52+ make mlx-start # start on port 8095 (foreground)
53+ make status # check current model state
54+ make mlx-stop # stop everything
3055```
3156
32- ## Project layout
57+ ### Testing
3358
59+ ``` bash
60+ make test # run full test suite (42 tests)
61+ make lint # ruff check + format check
3462```
35- src/
36- ├── main.py FastAPI app, lifespan, route registration
37- ├── config.py Reads models.yaml, exposes typed constants
38- ├── router.py All HTTP endpoints, cross-manager coordination
39- ├── process_manager.py Subprocess lifecycle (text/vision)
40- └── inline_manager.py In-process lifecycle (embedding/tts/stt)
41- models.yaml Operator-editable model registry
42- docs/ Full documentation
63+
64+ CI runs lint, format check, and tests on Python 3.11, 3.12, and 3.13 (macOS) for every push and PR.
65+
66+ ** Manual testing** — before submitting a PR that changes inference or model loading:
67+
68+ - [ ] Start the server: ` mlx-serve start `
69+ - [ ] Exercise the relevant model type with a real request
70+ - [ ] Verify ` curl http://localhost:8095/status ` shows correct state
71+ - [ ] Verify inactivity unload works (lower ` inactivity_timeout_seconds ` temporarily)
72+ - [ ] Verify ` mlx-serve stop ` cleanly terminates all processes
73+
74+ ### Code Style
75+
76+ We use [ ruff] ( https://docs.astral.sh/ruff/ ) for linting and formatting:
77+
78+ ``` bash
79+ uv run ruff check src/ # lint
80+ uv run ruff format src/ tests/ # format
4381```
4482
45- Key constraint: ` process_manager ` and ` inline_manager ` must not import each other. All cross-manager coordination belongs in ` router.py ` .
83+ Rules enforced: isort (import sorting), pyupgrade (modern syntax), flake8-comprehensions, flake8-simplify, flake8-use-pathlib. See ` pyproject.toml ` for the full config .
4684
47- ## Adding a new model type
85+ Style guidelines:
86+ - No new abstractions for single-use cases
87+ - No docstrings on functions whose purpose is obvious from the name
88+ - Error responses must follow OpenAI format: ` {"error": {"message": "...", "code": N}} `
89+ - Keep the two managers pure — no cross-imports
90+ - Use ` pathlib.Path ` over ` os.path `
91+ - Use ` datetime.UTC ` over ` timezone.utc `
92+
93+ ## Adding a New Model Type
4894
4995If you want to add support for a new MLX inference library:
5096
51971 . Add the new type string to ` _VALID_TYPES ` in ` config.py `
52982 . Add load/run/generate functions to ` inline_manager.py ` (for in-process) or extend ` process_manager.py ` (for subprocess)
53993 . Add a new endpoint in ` router.py `
541004 . Add the type to ` _TYPE_CAPABILITIES ` in ` router.py `
55- 5 . Update ` models.yaml ` comments and ` docs/configuration.md `
101+ 5 . Add tests for the new endpoint in ` tests/test_api.py `
102+ 6 . Update ` models.yaml ` comments and ` docs/configuration.md `
56103
57- ## Adding a model to models.yaml
104+ ## Adding a Model to models.yaml
58105
59106``` yaml
60107- name : my-model # name clients use in API requests
@@ -70,28 +117,74 @@ Download the model first:
70117uv run huggingface-cli download mlx-community/my-model-name
71118```
72119
73- ## Testing
120+ ## Pull Request Guidelines
74121
75- MLX requires Apple Silicon hardware — automated integration tests cannot run on GitHub Actions. The CI workflow validates Python syntax and YAML only.
122+ 1 . Fork and create a branch from ` main `
123+ 2 . Make your change — keep it focused, no unrelated cleanup
124+ 3 . Add or update tests for new/changed behavior
125+ 4 . Ensure ` make test ` and ` make lint ` pass
126+ 5 . Update docs if behavior changes
127+ 6 . Open a PR using the provided template
76128
77- Manual testing checklist before submitting a PR:
129+ ## Releases and PyPI Publishing
78130
79- - [ ] Start the manager: ` make mlx-start `
80- - [ ] Exercise the relevant model type with a real request
81- - [ ] Verify ` make status ` shows correct state
82- - [ ] Verify ` make mlx-stop ` cleanly terminates all processes
83- - [ ] Check that inactivity unload works (lower ` inactivity_timeout_seconds ` temporarily)
131+ ** Only maintainers** should create releases. mlx-serve uses an automated CI/CD pipeline for PyPI deployment.
84132
85- ## Submitting a PR
133+ ### Version Numbering
86134
87- 1 . Fork and create a branch from ` main `
88- 2 . Make your change — keep it focused, no unrelated cleanup
89- 3 . Update docs if behavior changes
90- 4 . Open a PR using the provided template
135+ Follow [ Semantic Versioning] ( https://semver.org/ ) :
91136
92- ## Style notes
137+ - ** Major (X.0.0)** : Breaking API changes
138+ - ** Minor (X.Y.0)** : New features, backward compatible
139+ - ** Patch (X.Y.Z)** : Bug fixes, backward compatible
93140
94- - No new abstractions for single-use cases
95- - No docstrings on functions whose purpose is obvious from the name
96- - Error responses must follow OpenAI format: ` {"error": {"message": "...", "code": N}} `
97- - Keep the two managers pure — no cross-imports
141+ ### Release Process
142+
143+ 1 . ** Update the version** :
144+ ``` bash
145+ # Edit src/mlx_serve/__init__.py
146+ __version__ = " X.Y.Z"
147+ ```
148+
149+ 2 . ** Update the changelog** :
150+ ``` bash
151+ # Add a new section to CHANGELOG.md with the changes
152+ ```
153+
154+ 3 . ** Commit and push** :
155+ ``` bash
156+ git add src/mlx_serve/__init__.py CHANGELOG.md
157+ git commit -m " Bump version to X.Y.Z"
158+ git push origin main
159+ ```
160+
161+ 4 . ** Tag and push the tag** :
162+ ``` bash
163+ git tag vX.Y.Z
164+ git push origin vX.Y.Z
165+ ```
166+
167+ 5 . ** Automated deployment** : Pushing the tag triggers the publish workflow which:
168+ - Verifies the tag matches ` __version__ `
169+ - Runs the full test suite
170+ - Builds the package (wheel + sdist)
171+ - Publishes to PyPI via Trusted Publishing
172+ - Creates a GitHub Release with auto-generated notes
173+
174+ ### Publish Pipeline
175+
176+ - ** Trigger** : Git tag push matching ` v* `
177+ - ** Workflow** : ` .github/workflows/publish.yml `
178+ - ** Authentication** : PyPI Trusted Publishing (OIDC) — no tokens needed
179+ - ** Jobs** : ` build ` (test + build) → ` publish ` (upload to PyPI) → ` github-release ` (create release)
180+
181+ ### Prerequisites for Maintainers
182+
183+ To deploy releases, maintainers need:
184+ 1 . Write access to the repository
185+ 2 . Permission to create ` v* ` tags
186+ 3 . PyPI Trusted Publisher configured for the ` mlx-serve ` project
187+
188+ ## License
189+
190+ By contributing to this project, you agree that your contributions will be licensed under the [ MIT License] ( LICENSE ) .
0 commit comments