mesh-deck 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mesh_deck-0.1.0/CHANGELOG.md +30 -0
- mesh_deck-0.1.0/CONTRIBUTING.md +101 -0
- mesh_deck-0.1.0/LICENSE +21 -0
- mesh_deck-0.1.0/MANIFEST.in +5 -0
- mesh_deck-0.1.0/PKG-INFO +238 -0
- mesh_deck-0.1.0/README.md +196 -0
- mesh_deck-0.1.0/SECURITY.md +20 -0
- mesh_deck-0.1.0/docs/assets/logo-dark.svg +14 -0
- mesh_deck-0.1.0/docs/assets/logo-light.svg +14 -0
- mesh_deck-0.1.0/docs/commands.md +215 -0
- mesh_deck-0.1.0/docs/config.md +91 -0
- mesh_deck-0.1.0/docs/how-it-works.md +95 -0
- mesh_deck-0.1.0/examples/dual-gpu.yaml +74 -0
- mesh_deck-0.1.0/examples/single-gpu.yaml +51 -0
- mesh_deck-0.1.0/pyproject.toml +77 -0
- mesh_deck-0.1.0/setup.cfg +4 -0
- mesh_deck-0.1.0/src/mesh_cli/__init__.py +1 -0
- mesh_deck-0.1.0/src/mesh_cli/__main__.py +7 -0
- mesh_deck-0.1.0/src/mesh_cli/cli.py +333 -0
- mesh_deck-0.1.0/src/mesh_cli/completion.py +192 -0
- mesh_deck-0.1.0/src/mesh_cli/config.py +96 -0
- mesh_deck-0.1.0/src/mesh_cli/gpu.py +88 -0
- mesh_deck-0.1.0/src/mesh_cli/holders.py +310 -0
- mesh_deck-0.1.0/src/mesh_cli/planner.py +113 -0
- mesh_deck-0.1.0/src/mesh_cli/py.typed +0 -0
- mesh_deck-0.1.0/src/mesh_cli/runner.py +388 -0
- mesh_deck-0.1.0/src/mesh_cli/suggest.py +17 -0
- mesh_deck-0.1.0/src/mesh_cli/ui.py +251 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/PKG-INFO +238 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/SOURCES.txt +44 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/dependency_links.txt +1 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/entry_points.txt +2 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/requires.txt +11 -0
- mesh_deck-0.1.0/src/mesh_deck.egg-info/top_level.txt +1 -0
- mesh_deck-0.1.0/tests/fixtures/compute_apps_running.csv +5 -0
- mesh_deck-0.1.0/tests/fixtures/gpu_query_running.csv +3 -0
- mesh_deck-0.1.0/tests/fixtures/gpu_query_util.csv +3 -0
- mesh_deck-0.1.0/tests/fixtures/nvidia_smi_processes.xml +30 -0
- mesh_deck-0.1.0/tests/test_cli.py +214 -0
- mesh_deck-0.1.0/tests/test_config.py +56 -0
- mesh_deck-0.1.0/tests/test_gpu.py +39 -0
- mesh_deck-0.1.0/tests/test_holders.py +233 -0
- mesh_deck-0.1.0/tests/test_planner.py +104 -0
- mesh_deck-0.1.0/tests/test_runner.py +305 -0
- mesh_deck-0.1.0/tests/test_suggest.py +152 -0
- mesh_deck-0.1.0/tests/test_ui.py +172 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
Nothing yet.
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-10-26
|
|
12
|
+
|
|
13
|
+
First public release, published on PyPI as `mesh-deck` (the command is `mesh`).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `mesh gpu`: shows each GPU's memory and utilization and who is holding the memory.
|
|
17
|
+
- Progress and completion messages when stopping services (`Ctrl+C` or `mesh down`).
|
|
18
|
+
- `mesh logs -n all` to print a whole log.
|
|
19
|
+
- `python -m mesh_cli` as an alternative to the `mesh` command.
|
|
20
|
+
- Terminal UI: colour, tables, memory bars and a live start-up line (spinner and time) when output is an
|
|
21
|
+
interactive terminal. Piped output and `NO_COLOR` keep the plain text. Adds the `rich` dependency.
|
|
22
|
+
- "Did you mean" hints for mistyped commands, service names, config fields and config file names.
|
|
23
|
+
- `mesh completion bash|zsh|fish` prints a Tab-completion script; `mesh logs` completes service names.
|
|
24
|
+
- `mesh up` prints a one-line plan summary when everything fits, and the full plan only when there is something
|
|
25
|
+
to read or with `--plan`.
|
|
26
|
+
- Type information is shipped with the package (`py.typed`).
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- A second Ctrl+C (or `mesh down`) while `mesh up` was stopping services no longer aborts the cleanup and leaves
|
|
30
|
+
services running.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Contributing to mesh-deck
|
|
2
|
+
|
|
3
|
+
Thanks for helping. This project is small and early, so the most useful contributions are real-world data and
|
|
4
|
+
bug reports. Please read [How it works](docs/how-it-works.md) first, especially "What it does not do", so you
|
|
5
|
+
know what is in scope.
|
|
6
|
+
|
|
7
|
+
## The most useful thing you can send: output from your GPU
|
|
8
|
+
|
|
9
|
+
The planner has only been checked on two RTX 4090s. If you have a different GPU, driver or vLLM version,
|
|
10
|
+
real output from it is worth more than code.
|
|
11
|
+
|
|
12
|
+
1. Run these two commands, ideally with some services running:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
nvidia-smi --query-gpu=index,uuid,name,memory.total,memory.used,memory.free --format=csv
|
|
16
|
+
nvidia-smi --query-compute-apps=gpu_uuid,pid,process_name,used_memory --format=csv
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If you can, also run `nvidia-smi -q -x` (it lists graphics processes such as the desktop) and keep only the
|
|
20
|
+
`<gpu>` blocks with their `<uuid>` and `<processes>`.
|
|
21
|
+
2. Replace the `uuid` values with dummy ones (for example `GPU-00000000-0000-0000-0000-000000000000`).
|
|
22
|
+
3. Open a pull request that adds the output to `tests/fixtures/` and a test in `tests/test_gpu.py` or
|
|
23
|
+
`tests/test_planner.py` that parses it. If the planned memory is wrong for your card, say so in the pull
|
|
24
|
+
request. That is a bug we want to know about.
|
|
25
|
+
|
|
26
|
+
## Reporting a bug
|
|
27
|
+
|
|
28
|
+
Open an issue with:
|
|
29
|
+
|
|
30
|
+
- `mesh --version`, your OS, GPU model(s), driver version, and the engine you run (for example vLLM and its version).
|
|
31
|
+
- The command you ran and the full output.
|
|
32
|
+
- Your `models.yaml` **with private paths and model names removed**.
|
|
33
|
+
- The two `nvidia-smi` outputs above, from just before the failure if you can.
|
|
34
|
+
|
|
35
|
+
For a service that fails to start, also include the last lines of its `logs/NAME.log`.
|
|
36
|
+
|
|
37
|
+
## Development setup
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/mrahmedromman/mesh-deck.git
|
|
41
|
+
cd mesh-deck
|
|
42
|
+
python3 -m venv .venv
|
|
43
|
+
source .venv/bin/activate
|
|
44
|
+
pip install -e ".[dev]"
|
|
45
|
+
pytest
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Before opening a pull request, also run the style and type checks that CI runs:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ruff check src tests
|
|
52
|
+
mypy
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The tests need no GPU and no model. Runner tests start throwaway `http.server` processes, so they take a few
|
|
56
|
+
seconds.
|
|
57
|
+
|
|
58
|
+
### Layout
|
|
59
|
+
|
|
60
|
+
| Path | What it holds |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `src/mesh_cli/config.py` | `models.yaml` loading and validation |
|
|
63
|
+
| `src/mesh_cli/gpu.py` | `nvidia-smi` output parsing |
|
|
64
|
+
| `src/mesh_cli/holders.py` | working out who holds GPU memory (`mesh gpu`) |
|
|
65
|
+
| `src/mesh_cli/planner.py` | the memory simulation |
|
|
66
|
+
| `src/mesh_cli/runner.py` | starting, health-checking, logging and stopping services |
|
|
67
|
+
| `src/mesh_cli/suggest.py` | "did you mean" hints |
|
|
68
|
+
| `src/mesh_cli/completion.py` | bash, zsh and fish completion scripts, generated from the argument parser |
|
|
69
|
+
| `src/mesh_cli/ui.py` | colour, tables, memory bars and the live start-up line (terminal only) |
|
|
70
|
+
| `src/mesh_cli/cli.py` | the `mesh` command |
|
|
71
|
+
| `tests/` | tests, with real `nvidia-smi` output in `tests/fixtures/` |
|
|
72
|
+
| `examples/`, `docs/` | example configs and the documentation |
|
|
73
|
+
|
|
74
|
+
## Pull requests
|
|
75
|
+
|
|
76
|
+
- **Keep them small** and about one thing.
|
|
77
|
+
- **Add or update a test** for any behaviour change. A bug fix should come with a test that fails without it.
|
|
78
|
+
- **Run `pytest`, `ruff check src tests` and `mypy`** before opening the pull request.
|
|
79
|
+
- **Add a line to `CHANGELOG.md`** under "Unreleased" for anything a user would notice.
|
|
80
|
+
- **Update the docs** if you change a command, an option or a config field: `README.md` and the pages in `docs/`.
|
|
81
|
+
- **Keep private details out.** Examples and fixtures must not contain your hostnames, home paths, model names
|
|
82
|
+
or GPU UUIDs. The examples use generic names (`llm`, `stt`, `tts`, `embed`) on purpose.
|
|
83
|
+
- Follow the style of the surrounding code: type hints, short functions, comments only where the reason is not
|
|
84
|
+
obvious.
|
|
85
|
+
|
|
86
|
+
## Scope
|
|
87
|
+
|
|
88
|
+
mesh checks GPU memory, starts services in order and keeps their logs. It deliberately does not proxy requests,
|
|
89
|
+
swap models on demand or replace a process manager. Tools such as llama-swap, LiteLLM and Docker Compose
|
|
90
|
+
already do those well. If you are unsure whether an idea fits, open an issue to ask before writing code.
|
|
91
|
+
|
|
92
|
+
Ideas that would fit well:
|
|
93
|
+
|
|
94
|
+
- Support for AMD GPUs (a parser for `rocm-smi`) and other vendors.
|
|
95
|
+
- Recognising Podman and Kubernetes containers in `mesh gpu`.
|
|
96
|
+
- Checking that other engines (not only vLLM) are planned correctly.
|
|
97
|
+
- Exporting a config for another tool, such as llama-swap.
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
By contributing you agree that your contributions are licensed under the [MIT License](LICENSE).
|
mesh_deck-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) AhmedRomman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mesh_deck-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mesh-deck
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Plan and start multi-service local AI stacks (vLLM, speech-to-text, embeddings) on shared GPUs without OOM surprises.
|
|
5
|
+
Author: AhmedRomman
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mrahmedromman/mesh-deck
|
|
8
|
+
Project-URL: Documentation, https://github.com/mrahmedromman/mesh-deck/tree/main/docs
|
|
9
|
+
Project-URL: Changelog, https://github.com/mrahmedromman/mesh-deck/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/mrahmedromman/mesh-deck/issues
|
|
11
|
+
Project-URL: Source, https://github.com/mrahmedromman/mesh-deck
|
|
12
|
+
Keywords: mesh-deck,vllm,gpu,llm,nvidia,cli,self-hosted,inference,vram,local-ai
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Classifier: Topic :: Software Development
|
|
27
|
+
Classifier: Topic :: System :: Systems Administration
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: pydantic>=2.5
|
|
32
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
33
|
+
Requires-Dist: rich>=13
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
38
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
39
|
+
Requires-Dist: build; extra == "dev"
|
|
40
|
+
Requires-Dist: twine; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<picture>
|
|
45
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/mrahmedromman/mesh-deck/main/docs/assets/logo-dark.svg">
|
|
46
|
+
<img alt="mesh-deck" src="https://raw.githubusercontent.com/mrahmedromman/mesh-deck/main/docs/assets/logo-light.svg" width="320">
|
|
47
|
+
</picture>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<p align="center"><b>Know your AI stack fits your GPUs before you start it.</b></p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img alt="license: MIT" src="https://img.shields.io/badge/license-MIT-blue">
|
|
54
|
+
<img alt="python: 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue">
|
|
55
|
+
<img alt="status: early" src="https://img.shields.io/badge/status-early-orange">
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<a href="#quickstart">Quickstart</a> ·
|
|
60
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/commands.md">Commands</a> ·
|
|
61
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/config.md">Config</a> ·
|
|
62
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/how-it-works.md">How it works</a> ·
|
|
63
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/CONTRIBUTING.md">Contributing</a>
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
`mesh` plans and starts a stack of local AI services (vLLM, speech-to-text, embeddings, TTS) on shared GPUs,
|
|
67
|
+
in the right order, with one log file per service.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
$ mesh up models.yaml
|
|
71
|
+
[1/4] starting llm on port 8001 (log: logs/llm.log)
|
|
72
|
+
llm is up (memory it added: GPU 0: ~16260 MiB, GPU 1: ~16260 MiB)
|
|
73
|
+
[2/4] starting stt on port 8004 (log: logs/stt.log)
|
|
74
|
+
stt is up (memory it added: GPU 0: ~6436 MiB)
|
|
75
|
+
[3/4] starting tts on port 8005 (log: logs/tts.log)
|
|
76
|
+
tts is up (memory it added: GPU 1: ~2353 MiB)
|
|
77
|
+
[4/4] starting embed on port 8003 (log: logs/embed.log)
|
|
78
|
+
embed is up (memory it added: GPU 1: ~3059 MiB)
|
|
79
|
+
All services are up. Press Ctrl+C to stop everything.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> **Early release.** Built and checked on one machine with two RTX 4090s. It reads `nvidia-smi`, so it needs an
|
|
83
|
+
> NVIDIA GPU and a Unix-like system (tested on Linux). Reports from other GPUs are very welcome.
|
|
84
|
+
|
|
85
|
+
## Why
|
|
86
|
+
|
|
87
|
+
If you run several vLLM or Docker services on one machine, you have probably hit this: the second service
|
|
88
|
+
crashes at startup with *"Free memory on device ... is less than desired GPU memory utilization"*. Each service
|
|
89
|
+
measures free memory when it starts, and the first one already took its share. Working out fractions that fit,
|
|
90
|
+
starting things in the right order, and finding which log holds the error is tedious to redo by hand.
|
|
91
|
+
|
|
92
|
+
## Features
|
|
93
|
+
|
|
94
|
+
- **Checks first.** Simulates GPU memory for your services in start order and tells you which will not fit,
|
|
95
|
+
with a suggested fraction.
|
|
96
|
+
- **Starts in order.** Each service waits for the previous one's health check, so they never race for memory.
|
|
97
|
+
- **Any runtime.** A service is a shell command: a venv, a conda environment or `docker run`.
|
|
98
|
+
- **One log per service.** `mesh logs llm -f` follows one; `-n all` prints the whole file.
|
|
99
|
+
- **Reports real memory.** After each start it prints how much GPU memory that service added.
|
|
100
|
+
- **Says who holds the memory.** `mesh gpu` shows each GPU's memory and utilization and names the holder: one of
|
|
101
|
+
your services, a Docker container, the desktop, or another program. It prints this automatically when a start
|
|
102
|
+
fails for lack of memory.
|
|
103
|
+
- **Clean shutdown.** One Ctrl+C (or `mesh down`) stops everything in reverse order, tells you as each service
|
|
104
|
+
stops, and ends with a completion message.
|
|
105
|
+
- **Readable in a terminal.** Colour, tables, memory bars, and a live line while a model loads (a spinner and
|
|
106
|
+
the time so far). When output is piped or `NO_COLOR` is set, it prints plain text.
|
|
107
|
+
- **Helpful errors and Tab completion.** A mistyped command, service name, config field or file gets a "did you
|
|
108
|
+
mean" hint, and `mesh completion bash|zsh|fish` adds Tab completion, including your service names.
|
|
109
|
+
- **Just a YAML file.** No daemon, no proxy, nothing listening on a port.
|
|
110
|
+
|
|
111
|
+
## Install
|
|
112
|
+
|
|
113
|
+
Use a separate environment from the one that runs your models; `mesh` needs only `pydantic`, `pyyaml` and `rich`.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pipx install mesh-deck # or: pip install mesh-deck
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
To run the latest unreleased code instead:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pipx install git+https://github.com/mrahmedromman/mesh-deck.git
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Quickstart
|
|
126
|
+
|
|
127
|
+
1. Write a `models.yaml` (see [examples/](https://github.com/mrahmedromman/mesh-deck/tree/main/examples/) for complete ones):
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
services:
|
|
131
|
+
- name: chat
|
|
132
|
+
port: 8001
|
|
133
|
+
gpus: [0]
|
|
134
|
+
gpu_memory_utilization: 0.55
|
|
135
|
+
overhead_mib: 1500
|
|
136
|
+
command: |
|
|
137
|
+
source ~/venvs/vllm/bin/activate
|
|
138
|
+
exec vllm serve /path/to/chat-model --port 8001 --gpu-memory-utilization 0.55
|
|
139
|
+
|
|
140
|
+
- name: embeddings
|
|
141
|
+
port: 8003
|
|
142
|
+
gpus: [0]
|
|
143
|
+
gpu_memory_utilization: 0.15
|
|
144
|
+
overhead_mib: 500
|
|
145
|
+
command: |
|
|
146
|
+
source ~/venvs/vllm/bin/activate
|
|
147
|
+
exec vllm serve /path/to/embedding-model --port 8003 --gpu-memory-utilization 0.15
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
2. Check that it fits, without starting anything:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
mesh plan models.yaml
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
3. Start it:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
mesh up models.yaml
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
4. In other terminals, from the same directory:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
mesh status # what is running, and whether it answers
|
|
166
|
+
mesh logs chat -f # follow one service's log
|
|
167
|
+
mesh down # stop everything (Ctrl+C in the `mesh up` terminal does the same)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
If the plan says a service will not fit, `mesh up` refuses to start unless you add `--force`. Output looks
|
|
171
|
+
like this (illustrative numbers):
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
2. embeddings FAIL
|
|
175
|
+
GPU 0: needs 3612 MiB free but only 2100 MiB is free
|
|
176
|
+
try --gpu-memory-utilization 0.08 or less
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The first time, run `mesh up` once and copy the printed "memory it added" numbers into `measured_mib`. The
|
|
180
|
+
planner is most accurate with measured numbers. See [Config](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/config.md#getting-real-numbers-for-measured_mib).
|
|
181
|
+
|
|
182
|
+
## Commands
|
|
183
|
+
|
|
184
|
+
| Command | What it does |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `mesh plan CONFIG` | Simulates starting the services in order and reports which ones fit. |
|
|
187
|
+
| `mesh up CONFIG` | Plans, starts each service in order, waits for its health check, and reports the memory it added. |
|
|
188
|
+
| `mesh down` | Stops a stack started by `mesh up`. |
|
|
189
|
+
| `mesh status` | Shows each service's state, process id, port and health. |
|
|
190
|
+
| `mesh logs NAME [-n N\|all] [-f]` | Shows or follows a service's log. |
|
|
191
|
+
| `mesh gpu` | Shows each GPU's memory and utilization, and who is holding the memory. |
|
|
192
|
+
| `mesh completion SHELL` | Prints a Tab-completion script for `bash`, `zsh` or `fish`. |
|
|
193
|
+
|
|
194
|
+
All options, `mesh logs` examples, exit codes and where files go: [docs/commands.md](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/commands.md).
|
|
195
|
+
|
|
196
|
+
## How it works
|
|
197
|
+
|
|
198
|
+
The driver keeps back some GPU memory, and vLLM measures free memory when each service starts. `mesh` subtracts
|
|
199
|
+
the reserved memory and anything other programs are using, then checks each service in order, reducing what is
|
|
200
|
+
free as services start. On two RTX 4090s the planned free memory matched the real `nvidia-smi` numbers to within
|
|
201
|
+
about 20 MiB once real footprints were entered. Details and limits: [docs/how-it-works.md](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/how-it-works.md).
|
|
202
|
+
|
|
203
|
+
## Compared with
|
|
204
|
+
|
|
205
|
+
- **Docker Compose** can order containers with `depends_on` and healthchecks. It is the right tool if
|
|
206
|
+
everything is a container. `mesh` also handles plain commands (a venv or conda environment) and checks
|
|
207
|
+
GPU memory before starting.
|
|
208
|
+
- **[llama-swap](https://github.com/mostlygeek/llama-swap)** swaps models on demand behind one endpoint.
|
|
209
|
+
`mesh` does not proxy requests or swap: it starts a fixed set of services that stay loaded.
|
|
210
|
+
- **A shell script** works fine for one machine. `mesh` replaces the parts that are tedious to redo: working
|
|
211
|
+
out the fractions, waiting on health checks, separate logs, and a clean shutdown.
|
|
212
|
+
|
|
213
|
+
## Security
|
|
214
|
+
|
|
215
|
+
`command` and `stop` run through `bash` with your user's permissions, so a `models.yaml` is a script. Only run
|
|
216
|
+
configs you trust. `mesh` itself sends nothing anywhere: it runs `nvidia-smi`, starts your commands, and checks
|
|
217
|
+
`127.0.0.1` ports.
|
|
218
|
+
|
|
219
|
+
## Limitations
|
|
220
|
+
|
|
221
|
+
- NVIDIA GPUs and Unix-like systems only. Not tested on Windows, AMD or Intel GPUs.
|
|
222
|
+
- No request proxy or single endpoint; each service keeps its own port.
|
|
223
|
+
- It does not restart a service that crashes after startup; it prints a warning.
|
|
224
|
+
- `mesh gpu` needs Linux and reports a snapshot only: no live view, no per-process utilization, no killing of
|
|
225
|
+
processes. It recognises Docker containers but not Podman or Kubernetes.
|
|
226
|
+
- Estimates for services nobody has measured are rough.
|
|
227
|
+
- Logs are overwritten on each `mesh up`, with no rotation.
|
|
228
|
+
|
|
229
|
+
## Contributing
|
|
230
|
+
|
|
231
|
+
Tests need no GPU: `pip install -e ".[dev]"` then `pytest`. The most useful contribution is real `nvidia-smi`
|
|
232
|
+
output from a GPU other than an RTX 4090, added as a test fixture. Bug reports and ideas are welcome as issues.
|
|
233
|
+
See [CONTRIBUTING.md](https://github.com/mrahmedromman/mesh-deck/blob/main/CONTRIBUTING.md) for the exact commands, what to include in a bug report, and what is in
|
|
234
|
+
scope.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
[MIT](https://github.com/mrahmedromman/mesh-deck/blob/main/LICENSE)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/mrahmedromman/mesh-deck/main/docs/assets/logo-dark.svg">
|
|
4
|
+
<img alt="mesh-deck" src="https://raw.githubusercontent.com/mrahmedromman/mesh-deck/main/docs/assets/logo-light.svg" width="320">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center"><b>Know your AI stack fits your GPUs before you start it.</b></p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img alt="license: MIT" src="https://img.shields.io/badge/license-MIT-blue">
|
|
12
|
+
<img alt="python: 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue">
|
|
13
|
+
<img alt="status: early" src="https://img.shields.io/badge/status-early-orange">
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="#quickstart">Quickstart</a> ·
|
|
18
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/commands.md">Commands</a> ·
|
|
19
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/config.md">Config</a> ·
|
|
20
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/docs/how-it-works.md">How it works</a> ·
|
|
21
|
+
<a href="https://github.com/mrahmedromman/mesh-deck/blob/main/CONTRIBUTING.md">Contributing</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
`mesh` plans and starts a stack of local AI services (vLLM, speech-to-text, embeddings, TTS) on shared GPUs,
|
|
25
|
+
in the right order, with one log file per service.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
$ mesh up models.yaml
|
|
29
|
+
[1/4] starting llm on port 8001 (log: logs/llm.log)
|
|
30
|
+
llm is up (memory it added: GPU 0: ~16260 MiB, GPU 1: ~16260 MiB)
|
|
31
|
+
[2/4] starting stt on port 8004 (log: logs/stt.log)
|
|
32
|
+
stt is up (memory it added: GPU 0: ~6436 MiB)
|
|
33
|
+
[3/4] starting tts on port 8005 (log: logs/tts.log)
|
|
34
|
+
tts is up (memory it added: GPU 1: ~2353 MiB)
|
|
35
|
+
[4/4] starting embed on port 8003 (log: logs/embed.log)
|
|
36
|
+
embed is up (memory it added: GPU 1: ~3059 MiB)
|
|
37
|
+
All services are up. Press Ctrl+C to stop everything.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **Early release.** Built and checked on one machine with two RTX 4090s. It reads `nvidia-smi`, so it needs an
|
|
41
|
+
> NVIDIA GPU and a Unix-like system (tested on Linux). Reports from other GPUs are very welcome.
|
|
42
|
+
|
|
43
|
+
## Why
|
|
44
|
+
|
|
45
|
+
If you run several vLLM or Docker services on one machine, you have probably hit this: the second service
|
|
46
|
+
crashes at startup with *"Free memory on device ... is less than desired GPU memory utilization"*. Each service
|
|
47
|
+
measures free memory when it starts, and the first one already took its share. Working out fractions that fit,
|
|
48
|
+
starting things in the right order, and finding which log holds the error is tedious to redo by hand.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Checks first.** Simulates GPU memory for your services in start order and tells you which will not fit,
|
|
53
|
+
with a suggested fraction.
|
|
54
|
+
- **Starts in order.** Each service waits for the previous one's health check, so they never race for memory.
|
|
55
|
+
- **Any runtime.** A service is a shell command: a venv, a conda environment or `docker run`.
|
|
56
|
+
- **One log per service.** `mesh logs llm -f` follows one; `-n all` prints the whole file.
|
|
57
|
+
- **Reports real memory.** After each start it prints how much GPU memory that service added.
|
|
58
|
+
- **Says who holds the memory.** `mesh gpu` shows each GPU's memory and utilization and names the holder: one of
|
|
59
|
+
your services, a Docker container, the desktop, or another program. It prints this automatically when a start
|
|
60
|
+
fails for lack of memory.
|
|
61
|
+
- **Clean shutdown.** One Ctrl+C (or `mesh down`) stops everything in reverse order, tells you as each service
|
|
62
|
+
stops, and ends with a completion message.
|
|
63
|
+
- **Readable in a terminal.** Colour, tables, memory bars, and a live line while a model loads (a spinner and
|
|
64
|
+
the time so far). When output is piped or `NO_COLOR` is set, it prints plain text.
|
|
65
|
+
- **Helpful errors and Tab completion.** A mistyped command, service name, config field or file gets a "did you
|
|
66
|
+
mean" hint, and `mesh completion bash|zsh|fish` adds Tab completion, including your service names.
|
|
67
|
+
- **Just a YAML file.** No daemon, no proxy, nothing listening on a port.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
Use a separate environment from the one that runs your models; `mesh` needs only `pydantic`, `pyyaml` and `rich`.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pipx install mesh-deck # or: pip install mesh-deck
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
To run the latest unreleased code instead:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pipx install git+https://github.com/mrahmedromman/mesh-deck.git
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quickstart
|
|
84
|
+
|
|
85
|
+
1. Write a `models.yaml` (see [examples/](https://github.com/mrahmedromman/mesh-deck/tree/main/examples/) for complete ones):
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
services:
|
|
89
|
+
- name: chat
|
|
90
|
+
port: 8001
|
|
91
|
+
gpus: [0]
|
|
92
|
+
gpu_memory_utilization: 0.55
|
|
93
|
+
overhead_mib: 1500
|
|
94
|
+
command: |
|
|
95
|
+
source ~/venvs/vllm/bin/activate
|
|
96
|
+
exec vllm serve /path/to/chat-model --port 8001 --gpu-memory-utilization 0.55
|
|
97
|
+
|
|
98
|
+
- name: embeddings
|
|
99
|
+
port: 8003
|
|
100
|
+
gpus: [0]
|
|
101
|
+
gpu_memory_utilization: 0.15
|
|
102
|
+
overhead_mib: 500
|
|
103
|
+
command: |
|
|
104
|
+
source ~/venvs/vllm/bin/activate
|
|
105
|
+
exec vllm serve /path/to/embedding-model --port 8003 --gpu-memory-utilization 0.15
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. Check that it fits, without starting anything:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
mesh plan models.yaml
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
3. Start it:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mesh up models.yaml
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
4. In other terminals, from the same directory:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
mesh status # what is running, and whether it answers
|
|
124
|
+
mesh logs chat -f # follow one service's log
|
|
125
|
+
mesh down # stop everything (Ctrl+C in the `mesh up` terminal does the same)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If the plan says a service will not fit, `mesh up` refuses to start unless you add `--force`. Output looks
|
|
129
|
+
like this (illustrative numbers):
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
2. embeddings FAIL
|
|
133
|
+
GPU 0: needs 3612 MiB free but only 2100 MiB is free
|
|
134
|
+
try --gpu-memory-utilization 0.08 or less
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The first time, run `mesh up` once and copy the printed "memory it added" numbers into `measured_mib`. The
|
|
138
|
+
planner is most accurate with measured numbers. See [Config](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/config.md#getting-real-numbers-for-measured_mib).
|
|
139
|
+
|
|
140
|
+
## Commands
|
|
141
|
+
|
|
142
|
+
| Command | What it does |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `mesh plan CONFIG` | Simulates starting the services in order and reports which ones fit. |
|
|
145
|
+
| `mesh up CONFIG` | Plans, starts each service in order, waits for its health check, and reports the memory it added. |
|
|
146
|
+
| `mesh down` | Stops a stack started by `mesh up`. |
|
|
147
|
+
| `mesh status` | Shows each service's state, process id, port and health. |
|
|
148
|
+
| `mesh logs NAME [-n N\|all] [-f]` | Shows or follows a service's log. |
|
|
149
|
+
| `mesh gpu` | Shows each GPU's memory and utilization, and who is holding the memory. |
|
|
150
|
+
| `mesh completion SHELL` | Prints a Tab-completion script for `bash`, `zsh` or `fish`. |
|
|
151
|
+
|
|
152
|
+
All options, `mesh logs` examples, exit codes and where files go: [docs/commands.md](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/commands.md).
|
|
153
|
+
|
|
154
|
+
## How it works
|
|
155
|
+
|
|
156
|
+
The driver keeps back some GPU memory, and vLLM measures free memory when each service starts. `mesh` subtracts
|
|
157
|
+
the reserved memory and anything other programs are using, then checks each service in order, reducing what is
|
|
158
|
+
free as services start. On two RTX 4090s the planned free memory matched the real `nvidia-smi` numbers to within
|
|
159
|
+
about 20 MiB once real footprints were entered. Details and limits: [docs/how-it-works.md](https://github.com/mrahmedromman/mesh-deck/blob/main/docs/how-it-works.md).
|
|
160
|
+
|
|
161
|
+
## Compared with
|
|
162
|
+
|
|
163
|
+
- **Docker Compose** can order containers with `depends_on` and healthchecks. It is the right tool if
|
|
164
|
+
everything is a container. `mesh` also handles plain commands (a venv or conda environment) and checks
|
|
165
|
+
GPU memory before starting.
|
|
166
|
+
- **[llama-swap](https://github.com/mostlygeek/llama-swap)** swaps models on demand behind one endpoint.
|
|
167
|
+
`mesh` does not proxy requests or swap: it starts a fixed set of services that stay loaded.
|
|
168
|
+
- **A shell script** works fine for one machine. `mesh` replaces the parts that are tedious to redo: working
|
|
169
|
+
out the fractions, waiting on health checks, separate logs, and a clean shutdown.
|
|
170
|
+
|
|
171
|
+
## Security
|
|
172
|
+
|
|
173
|
+
`command` and `stop` run through `bash` with your user's permissions, so a `models.yaml` is a script. Only run
|
|
174
|
+
configs you trust. `mesh` itself sends nothing anywhere: it runs `nvidia-smi`, starts your commands, and checks
|
|
175
|
+
`127.0.0.1` ports.
|
|
176
|
+
|
|
177
|
+
## Limitations
|
|
178
|
+
|
|
179
|
+
- NVIDIA GPUs and Unix-like systems only. Not tested on Windows, AMD or Intel GPUs.
|
|
180
|
+
- No request proxy or single endpoint; each service keeps its own port.
|
|
181
|
+
- It does not restart a service that crashes after startup; it prints a warning.
|
|
182
|
+
- `mesh gpu` needs Linux and reports a snapshot only: no live view, no per-process utilization, no killing of
|
|
183
|
+
processes. It recognises Docker containers but not Podman or Kubernetes.
|
|
184
|
+
- Estimates for services nobody has measured are rough.
|
|
185
|
+
- Logs are overwritten on each `mesh up`, with no rotation.
|
|
186
|
+
|
|
187
|
+
## Contributing
|
|
188
|
+
|
|
189
|
+
Tests need no GPU: `pip install -e ".[dev]"` then `pytest`. The most useful contribution is real `nvidia-smi`
|
|
190
|
+
output from a GPU other than an RTX 4090, added as a test fixture. Bug reports and ideas are welcome as issues.
|
|
191
|
+
See [CONTRIBUTING.md](https://github.com/mrahmedromman/mesh-deck/blob/main/CONTRIBUTING.md) for the exact commands, what to include in a bug report, and what is in
|
|
192
|
+
scope.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
[MIT](https://github.com/mrahmedromman/mesh-deck/blob/main/LICENSE)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Please do not open a public issue for a security problem. Use GitHub's private reporting instead: open the
|
|
6
|
+
repository's **Security** tab and choose **Report a vulnerability**.
|
|
7
|
+
|
|
8
|
+
Include the `mesh --version`, what you did, and what you expected. You can expect a reply within a week.
|
|
9
|
+
|
|
10
|
+
## What counts
|
|
11
|
+
|
|
12
|
+
`mesh` runs the `command` and `stop` lines of a `models.yaml` through `bash` with your user's permissions, by design.
|
|
13
|
+
A config file is a script, so running a config you do not trust is not a vulnerability in `mesh`.
|
|
14
|
+
|
|
15
|
+
In scope: a way to make `mesh` run something that is not in the config, write outside the log directory, or signal
|
|
16
|
+
processes that do not belong to the stack.
|
|
17
|
+
|
|
18
|
+
## Supported versions
|
|
19
|
+
|
|
20
|
+
Only the latest release receives fixes while the project is in alpha.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="340" height="96" viewBox="0 0 340 96" role="img" aria-label="mesh-deck">
|
|
2
|
+
<title>mesh-deck</title>
|
|
3
|
+
<!-- Two GPUs as tall outlines. Coloured blocks are services sharing the memory; the empty top is free memory. -->
|
|
4
|
+
<rect x="8" y="8" width="32" height="80" rx="6" fill="none" stroke="#d1d5db" stroke-width="3"/>
|
|
5
|
+
<rect x="12" y="50" width="24" height="34" rx="3" fill="#60a5fa"/>
|
|
6
|
+
<rect x="12" y="28" width="24" height="20" rx="3" fill="#2dd4bf"/>
|
|
7
|
+
|
|
8
|
+
<rect x="48" y="8" width="32" height="80" rx="6" fill="none" stroke="#d1d5db" stroke-width="3"/>
|
|
9
|
+
<rect x="52" y="50" width="24" height="34" rx="3" fill="#60a5fa"/>
|
|
10
|
+
<rect x="52" y="36" width="24" height="12" rx="3" fill="#fbbf24"/>
|
|
11
|
+
<rect x="52" y="22" width="24" height="12" rx="3" fill="#a78bfa"/>
|
|
12
|
+
|
|
13
|
+
<text x="100" y="62" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace" font-size="40" font-weight="700" fill="#f9fafb">mesh-deck</text>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="340" height="96" viewBox="0 0 340 96" role="img" aria-label="mesh-deck">
|
|
2
|
+
<title>mesh-deck</title>
|
|
3
|
+
<!-- Two GPUs as tall outlines. Coloured blocks are services sharing the memory; the empty top is free memory. -->
|
|
4
|
+
<rect x="8" y="8" width="32" height="80" rx="6" fill="none" stroke="#374151" stroke-width="3"/>
|
|
5
|
+
<rect x="12" y="50" width="24" height="34" rx="3" fill="#3b82f6"/>
|
|
6
|
+
<rect x="12" y="28" width="24" height="20" rx="3" fill="#14b8a6"/>
|
|
7
|
+
|
|
8
|
+
<rect x="48" y="8" width="32" height="80" rx="6" fill="none" stroke="#374151" stroke-width="3"/>
|
|
9
|
+
<rect x="52" y="50" width="24" height="34" rx="3" fill="#3b82f6"/>
|
|
10
|
+
<rect x="52" y="36" width="24" height="12" rx="3" fill="#f59e0b"/>
|
|
11
|
+
<rect x="52" y="22" width="24" height="12" rx="3" fill="#8b5cf6"/>
|
|
12
|
+
|
|
13
|
+
<text x="100" y="62" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace" font-size="40" font-weight="700" fill="#111827">mesh-deck</text>
|
|
14
|
+
</svg>
|