Contributing¶
Thanks for helping improve DGXMM. This page covers the development setup. Read the architecture overview first for how the pieces fit together.
Backend (Python 3.12+)¶
python3 -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/pytest -q # backend + installer tests (or: make test)
The tests need no GPU, no Docker daemon and no network. Docker, NVML, engines, the decision model and
the Hub are faked (tests/fakes.py). The installer tests run install.sh in a temporary copy of the
repo against stub commands (tests/fixtures/install_stubs/). The suite also includes:
tests/test_repo_hygiene.py: tracked files must not contain the development machine's user name, host name, private or Tailscale IP addresses, or names on its forbidden list (some are stored only as hashes). Use the placeholders it names (dgxuser,dgx-host, the documentation ranges192.0.2.0/24,198.51.100.0/24,203.0.113.0/24). Fixtures may name only public Hugging Face repositories and need a provenance note.tests/test_public_repo.py: what a public repository needs: engine, base and CI images pinned by digest, local state (.env,config.yaml,data/,secrets/) gitignored, no tracked file over 1 MB, the security policy, the issue forms and pull request template, no dependabot version updates.tests/test_docs_links.py: every relative link and#anchorin the README, CHANGELOG, CONTRIBUTING, SECURITY and the pages underdocs/resolves, links to the documentation site point at existing pages and headings, and the Decision model page's Docs link points at its page on the site.tests/test_docs_site.py: every page inmkdocs.yml's nav exists, every page underdocs/is in the nav, and the pinned documentation requirements carry hashes.tests/test_frontend_hygiene.py: no compiled.jsnext to the TypeScript sources.tests/test_real_corpus.pyandtests/test_decider_ops_corpora.py: the decision-model rules against the real-data corpora intests/fixtures/(see docs/decider-evaluation.md).tests/test_advanced_docs.py: the overview table ofdocs/decider-evaluation.mdhas one row per feature group with the registry's threshold, anddgxmm/decider/protocol.pynotes every threshold.tests/test_advanced_routes.py,tests/test_advanced_jobs.py,tests/test_advanced_event_loop.py: every route of the newer decision-model features in one anonymous / viewer / admin / CSRF matrix and rate-limited; the background jobs' budgets and that they cannot starve interactive questions; no blocking database or file work on the event loop in their async code.
Run one file with .venv/bin/pytest -q tests/test_docs_links.py.
Pylint. CI (.github/workflows/pylint.yml, Python 3.12) runs pylint --errors-only dgxmm, configured
in pyproject.toml ([tool.pylint]). Locally: .venv/bin/pip install pylint && .venv/bin/pylint --errors-only dgxmm.
CI Tests. The .github/workflows/tests.yml workflow runs on every push and pull request to main, with
three jobs: backend (pytest on Python 3.12; the whole suite, so the installer, docs-link, hygiene and
public-repo tests too), frontend (vitest, then the production build, on Node 22) and shellcheck (pinned
koalaman/shellcheck:v0.11.0) on every shell script. Backend tests install dev dependencies (pip install -e '.[dev]') for pytest-asyncio. Tests
that need Docker or GPU are skipped when those aren't available. tests/test_public_repo.py checks what
the public repository needs: images pinned by digest, local state gitignored, the security policy and
the issue and pull request templates. To lint the workflows themselves:
docker run --rm -v "$PWD:/repo:ro" -w /repo rhysd/actionlint:latest.
Shell scripts. make lint runs shellcheck on the installer and its test stubs in a container. To
check every shell script:
docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 \
install.sh tests/fixtures/install_stubs/* deploy/decider/entrypoint.sh scripts/*.sh
To try backend changes on a real box, rebuild and restart the container with ./install.sh. Do not
start a second backend from source on a host where DGXMM is installed: every instance adopts the
containers labelled dgxmm.managed=true.
Frontend (Node 22)¶
cd frontend
npm ci
npx vitest run # unit and component tests (including theme contrast checks)
npx tsc --noEmit # type-check only
npm run build # type-check + production build into frontend/dist
Without Node on the host, run the same commands in a container from the repo root:
docker run --rm -u "$(id -u):$(id -g)" -v "$PWD/frontend:/app" -w /app -e HOME=/tmp \
node:22-bookworm-slim sh -c "npm ci && npx vitest run && npm run build"
Dev server against a running backend. Vite serves the UI with hot reload at
http://127.0.0.1:5173/dgxmm/ and forwards /dgxmm/api to http://127.0.0.1:8090. That is the
installed DGXMM on the same host, so sign in with a normal DGXMM account:
cd frontend && npm run dev
# or, without Node on the host:
docker run --rm -it --network host -u "$(id -u):$(id -g)" -v "$PWD/frontend:/app" -w /app -e HOME=/tmp \
node:22-bookworm-slim sh -c "npm ci && npm run dev -- --host 127.0.0.1"
To work from a laptop, forward the port (ssh -L 5173:127.0.0.1:5173 <dgx-host>).
Documentation site¶
The site at https://nikghodki.github.io/dgxmodelmanager/ is built from docs/ with MkDocs Material
(mkdocs.yml, pinned with hashes in requirements-docs.txt) and published by
.github/workflows/pages.yml on every push to main that touches the documentation. Pull requests build it
with mkdocs build --strict (the docs job of the tests workflow), which fails on any warning, broken link
or missing #anchor.
.venv/bin/pip install --require-hashes -r requirements-docs.txt
.venv/bin/mkdocs serve # http://127.0.0.1:8000/dgxmodelmanager/, reloads on save
.venv/bin/mkdocs build --strict # what CI runs
- A new page goes into
docs/<section>/and intonav:inmkdocs.yml, with a front-matterdescription:and a "Next steps" list where it helps. - Headings get GitHub's ids on the site too, so
page.md#headinglinks work in both places. CHANGELOG.md,CONTRIBUTING.mdandSECURITY.mdstay at the repository root and are included bydocs/overrides/hooks/root_files.py, which rewrites theirdocs/...links for the site.- Diagrams are Mermaid code blocks; Mermaid itself is served from the site
(
docs/overrides/hooks/mermaid.py), so pages load nothing from a CDN. - Screenshots come in pairs (
name-light.png#only-light,name-dark.png#only-dark), and the site shows the one that matches the reader's theme.
End-to-end check¶
scripts/validate_on_box.sh drives a running DGXMM through its API, and it deploys and removes small
models, so warn other users of the box first:
Adding a decision-model feature¶
Every feature follows the same pattern; the existing ones in dgxmm/decider/ are the examples.
- Register it in
dgxmm/decider/registry.pywith oneregister(FeatureSpec(name, group, label, description, default_threshold, background=…, needs_model=…, viewer_visible=…)). That gives it a switch and threshold on the Decision model page, the settings keysdecider.feature.<name>.*, a rate-limit bucketdecider_<name>and the client priority. Add its threshold note todgxmm/decider/protocol.pyand a row to the overview table ofdocs/decider-evaluation.md(tests/test_advanced_docs.pychecks both). - Rules first. Write the deterministic rules and make the feature work with the decision model off.
Ask the model only where the rules have no answer, through
Decider.decidewith a constant question, an option set built by code and astatein which untrusted text is quoted, capped and redacted (quoted_blockandsafe_decideindgxmm/decider/common.py). Use the answer only at or above the threshold; explanations shown to users are templates, never model text. Log rules decisions too (record_rules/arecord_rules). - Background work goes through the insight engine:
ctx.insight_engine.register_job(name, feature, schedule, fn, budget_calls=…)inwire_features(dgxmm/decider/features.py) orwire_operations. Findings are insights (ctx.insights.upsert(..., dedupe_key=…, action=…)) with an action descriptor that opens an existing dialog; the server never executes it. - Routes depend on
limited(feature_bucket(name), admin=…)(dgxmm/api/ratelimit.py), which checks the role and the rate limit; state changes need the CSRF header like every other route. Keep blocking work off the event loop (asyncio.to_thread). Add them to the matrix intests/test_advanced_routes.py. - Evaluate it. Put a corpus in
tests/fixtures/decider_corpus/<name>/(public repos only, with a provenance note), addeval_case(case)to the feature's module, and run.venv/bin/python tools/eval_feature.py <name> default --rules-only, then against a decider with--base http://<decider>:<port> --key-file data/secrets/decider_api_key --rate 1. Choose the threshold from the Wilson 95 % lower bound and record the method, the numbers and the shortfalls indocs/decider-evaluation.md. - Document it in
docs/user-guide/decision-model.md,docs/configuration/decision-model.md(any fixed values) anddocs/security.md(new inputs, routes and what an answer can change).
Code layout¶
dgxmm/ FastAPI backend (module map: docs/architecture/index.md#backend-module-map)
api/ HTTP routers, one per area
deployer/ deployment lifecycle, states, reconcile, adopt
engines/ vLLM, llama.cpp and decision-model plug-ins
decider/ decision model: client, supervisor, image, snapshot, key, feature registry, insights,
job engine, and one module per feature
memory/ budget, estimates, calibration, host probes
planner/ catalog/ inventory/ metrics/ advisor/ auth/
frontend/src/
pages/ one component per route
components/ui/ shadcn/ui primitives
components/app/ app building blocks (shell, tables, memory bar, plan chooser, palette)
lib/ hooks/ pure helpers and hooks (with *.test.ts next to them)
api.ts auth.tsx stream.ts types.ts
tests/ pytest suite (fixtures/ holds the real-data corpora)
install.sh installer (see ./install.sh --help)
docs/ the documentation site's pages (mkdocs.yml); docs/superpowers/ holds design history
deploy/ example nginx config, bundled LiteLLM config (deploy/litellm/), decider image (deploy/decider/)
scripts/ end-to-end check, decider image and evaluation tools (scripts/decider_eval/)
Conventions:
- Only
dgxmm/docker_ctl.pytalks to Docker, and every write must keep thedgxmm.managed=trueguard. - New engine flags go into the allowlist in
dgxmm/engines/argguard.py, never into free-form strings. - Memory estimates must stay conservative. Calibration may raise them, never lower them.
- In the UI, every status shows an icon and text (never colour alone), and every icon-only button has an
aria-label. Use the shared dialogs, notalert()/confirm(). - The repo-root
.gitignorematcheslib/, so new files underfrontend/src/lib/needgit add -f.
Commits and pull requests¶
Report bugs and ideas with the issue forms, and security problems privately as described in SECURITY.md.
- Use Conventional Commits:
feat(scope): …,fix(ui): …,docs: …,test: …,style: …,chore: …. - Keep
.venv/bin/pytest -q,npx vitest runandnpm run buildgreen, and shellcheck clean. Add tests with every behaviour change. - Decision-model features must stay advisory and fail open: rules first, the model only above its threshold, and no change without an admin's confirmation (see docs/security.md).
- Update the docs in
docs/when you change behaviour, configuration keys or installer options. - Add user-visible changes to the
[Unreleased]section of CHANGELOG.md. - Screenshots in
docs/images/must come from a demo instance with made-up data, never from a real box (they would show deployment, container and repository names and command lines). - Never commit
.env,config.yaml,data/orsecrets/(they are gitignored).