localml 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.
Files changed (73) hide show
  1. localml-0.1.0/.env.example +26 -0
  2. localml-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
  3. localml-0.1.0/.github/actions/setup-uv/action.yml +20 -0
  4. localml-0.1.0/.github/pull_request_template.md +10 -0
  5. localml-0.1.0/.github/workflows/ci.yml +62 -0
  6. localml-0.1.0/.github/workflows/docs.yml +60 -0
  7. localml-0.1.0/.github/workflows/release.yml +76 -0
  8. localml-0.1.0/.github/workflows/zizmor.yml +26 -0
  9. localml-0.1.0/.gitignore +33 -0
  10. localml-0.1.0/.pre-commit-config.yaml +20 -0
  11. localml-0.1.0/.python-version +1 -0
  12. localml-0.1.0/CHANGELOG.md +13 -0
  13. localml-0.1.0/CONTRIBUTING.md +45 -0
  14. localml-0.1.0/LICENSE +21 -0
  15. localml-0.1.0/Makefile +37 -0
  16. localml-0.1.0/PKG-INFO +180 -0
  17. localml-0.1.0/README.md +142 -0
  18. localml-0.1.0/ROADMAP.md +142 -0
  19. localml-0.1.0/SECURITY.md +6 -0
  20. localml-0.1.0/docker-compose.yml +65 -0
  21. localml-0.1.0/docs/design.md +405 -0
  22. localml-0.1.0/docs/getting-started.md +35 -0
  23. localml-0.1.0/docs/index.md +43 -0
  24. localml-0.1.0/docs/reference/api.md +22 -0
  25. localml-0.1.0/docs/reference/index.md +3 -0
  26. localml-0.1.0/examples/quickstart.py +70 -0
  27. localml-0.1.0/pyproject.toml +116 -0
  28. localml-0.1.0/scripts/reset.py +22 -0
  29. localml-0.1.0/scripts/seed.py +26 -0
  30. localml-0.1.0/services/api/Dockerfile +12 -0
  31. localml-0.1.0/services/api/app/__init__.py +1 -0
  32. localml-0.1.0/services/api/app/auth.py +22 -0
  33. localml-0.1.0/services/api/app/config.py +23 -0
  34. localml-0.1.0/services/api/app/db.py +175 -0
  35. localml-0.1.0/services/api/app/lifecycle.py +46 -0
  36. localml-0.1.0/services/api/app/main.py +35 -0
  37. localml-0.1.0/services/api/app/queue.py +38 -0
  38. localml-0.1.0/services/api/app/routers/__init__.py +1 -0
  39. localml-0.1.0/services/api/app/routers/deployments.py +78 -0
  40. localml-0.1.0/services/api/app/routers/evaluations.py +57 -0
  41. localml-0.1.0/services/api/app/routers/models.py +77 -0
  42. localml-0.1.0/services/api/app/routers/projects.py +31 -0
  43. localml-0.1.0/services/api/app/routers/runs.py +80 -0
  44. localml-0.1.0/services/api/app/schemas.py +105 -0
  45. localml-0.1.0/services/api/app/store.py +95 -0
  46. localml-0.1.0/services/api/requirements.txt +9 -0
  47. localml-0.1.0/services/mlflow/Dockerfile +15 -0
  48. localml-0.1.0/services/worker/Dockerfile +10 -0
  49. localml-0.1.0/services/worker/evaluator.py +35 -0
  50. localml-0.1.0/services/worker/requirements.txt +5 -0
  51. localml-0.1.0/services/worker/worker.py +69 -0
  52. localml-0.1.0/src/localml/__init__.py +62 -0
  53. localml-0.1.0/src/localml/_state.py +31 -0
  54. localml-0.1.0/src/localml/adapters/__init__.py +6 -0
  55. localml-0.1.0/src/localml/adapters/base.py +54 -0
  56. localml-0.1.0/src/localml/cli.py +62 -0
  57. localml-0.1.0/src/localml/client.py +208 -0
  58. localml-0.1.0/src/localml/config.py +97 -0
  59. localml-0.1.0/src/localml/exceptions.py +35 -0
  60. localml-0.1.0/src/localml/huggingface.py +33 -0
  61. localml-0.1.0/src/localml/jax.py +48 -0
  62. localml-0.1.0/src/localml/mlx.py +32 -0
  63. localml-0.1.0/src/localml/ops.py +75 -0
  64. localml-0.1.0/src/localml/py.typed +0 -0
  65. localml-0.1.0/src/localml/run.py +39 -0
  66. localml-0.1.0/src/localml/torch.py +44 -0
  67. localml-0.1.0/src/localml/types.py +98 -0
  68. localml-0.1.0/tests/conftest.py +33 -0
  69. localml-0.1.0/tests/test_api.py +80 -0
  70. localml-0.1.0/tests/test_lifecycle.py +37 -0
  71. localml-0.1.0/tests/test_sdk.py +69 -0
  72. localml-0.1.0/uv.lock +3557 -0
  73. localml-0.1.0/zensical.toml +51 -0
@@ -0,0 +1,26 @@
1
+ # Copy to .env and adjust as needed. These defaults work with docker-compose.yml.
2
+
3
+ # Control plane
4
+ LOCALML_API_TOKEN=local-dev-token
5
+ LOCALML_AUTH_BYPASS=true
6
+
7
+ # Postgres
8
+ POSTGRES_USER=localml
9
+ POSTGRES_PASSWORD=localml
10
+ POSTGRES_DB=localml
11
+ DATABASE_URL=postgresql+psycopg://localml:localml@postgres:5432/localml
12
+
13
+ # Redis
14
+ REDIS_URL=redis://redis:6379/0
15
+
16
+ # MinIO (S3-compatible)
17
+ MINIO_ROOT_USER=minioadmin
18
+ MINIO_ROOT_PASSWORD=minioadmin
19
+ MINIO_ENDPOINT=http://minio:9000
20
+ MINIO_BUCKET=localml-artifacts
21
+
22
+ # MLflow
23
+ MLFLOW_TRACKING_URI=http://mlflow:5000
24
+
25
+ # Serving runtime
26
+ SERVING_URL=http://serving:11434
@@ -0,0 +1,29 @@
1
+ name: Bug report
2
+ description: Report a reproducible problem with localml.
3
+ title: "Bug: "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: textarea
7
+ id: description
8
+ attributes:
9
+ label: Description
10
+ description: What happened?
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: reproduce
15
+ attributes:
16
+ label: Reproduction
17
+ description: Steps, commands, or code needed to reproduce the issue.
18
+ validations:
19
+ required: true
20
+ - type: input
21
+ id: version
22
+ attributes:
23
+ label: localml version
24
+ placeholder: "0.1.0"
25
+ - type: textarea
26
+ id: environment
27
+ attributes:
28
+ label: Environment
29
+ description: Python version, OS, architecture, and relevant dependency versions.
@@ -0,0 +1,20 @@
1
+ name: Set up uv with Python
2
+ description: Install uv and Python with the project's pinned defaults.
3
+
4
+ inputs:
5
+ python-version:
6
+ description: Python version to install. Defaults to the project's floor.
7
+ required: false
8
+ default: "3.11"
9
+ enable-cache:
10
+ description: Pass-through to astral-sh/setup-uv enable-cache.
11
+ required: false
12
+ default: "false"
13
+
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
18
+ with:
19
+ python-version: ${{ inputs.python-version }}
20
+ enable-cache: ${{ inputs.enable-cache }}
@@ -0,0 +1,10 @@
1
+ ## Summary
2
+
3
+ -
4
+
5
+ ## Checks
6
+
7
+ - [ ] `uv run pytest`
8
+ - [ ] `uv run ruff check`
9
+ - [ ] `uv run ruff format --check`
10
+ - [ ] `uv run ty check src/`
@@ -0,0 +1,62 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ env:
16
+ UV_FROZEN: true
17
+
18
+ jobs:
19
+ lint:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 10
22
+ steps:
23
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
24
+ with:
25
+ persist-credentials: false
26
+ - uses: ./.github/actions/setup-uv
27
+ with:
28
+ enable-cache: ${{ github.event_name == 'push' }}
29
+ - run: uv sync
30
+ - run: uv run ruff check
31
+ - run: uv run ruff format --check
32
+ - run: uv run ty check src/
33
+
34
+ test:
35
+ runs-on: ubuntu-latest
36
+ timeout-minutes: 10
37
+ strategy:
38
+ fail-fast: false
39
+ matrix:
40
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
41
+ steps:
42
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
43
+ with:
44
+ persist-credentials: false
45
+ - uses: ./.github/actions/setup-uv
46
+ with:
47
+ python-version: ${{ matrix.python-version }}
48
+ enable-cache: ${{ github.event_name == 'push' }}
49
+ - run: uv sync
50
+ - run: uv run pytest ${{ matrix.python-version != '3.11' && '--no-cov' || '' }}
51
+
52
+ audit:
53
+ runs-on: ubuntu-latest
54
+ timeout-minutes: 10
55
+ steps:
56
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
57
+ with:
58
+ persist-credentials: false
59
+ - uses: ./.github/actions/setup-uv
60
+ with:
61
+ enable-cache: ${{ github.event_name == 'push' }}
62
+ - run: uvx pip-audit --require-hashes --strict -r <(uv pip compile pyproject.toml --group dev --generate-hashes)
@@ -0,0 +1,60 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: pages-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ env:
17
+ UV_FROZEN: true
18
+
19
+ jobs:
20
+ build:
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 10
23
+ permissions:
24
+ contents: read
25
+ pages: write
26
+ id-token: write
27
+ steps:
28
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
29
+ with:
30
+ persist-credentials: false
31
+ lfs: true
32
+ - uses: ./.github/actions/setup-uv
33
+ - run: uv sync
34
+ - name: Build docs
35
+ run: uv run zensical build
36
+ - name: Verify docs output
37
+ run: test -f site/index.html
38
+ - name: Setup Pages
39
+ if: github.event_name != 'pull_request'
40
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
41
+ - name: Upload artifact
42
+ if: github.event_name != 'pull_request'
43
+ uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
44
+ with:
45
+ path: site/
46
+
47
+ deploy:
48
+ needs: build
49
+ if: github.event_name != 'pull_request'
50
+ runs-on: ubuntu-latest
51
+ timeout-minutes: 5
52
+ permissions:
53
+ pages: write
54
+ id-token: write
55
+ environment:
56
+ name: github-pages
57
+ url: ${{ steps.deployment.outputs.page_url }}
58
+ steps:
59
+ - id: deployment
60
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
@@ -0,0 +1,76 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ concurrency:
8
+ group: ${{ github.workflow }}-${{ github.ref }}
9
+ cancel-in-progress: false
10
+
11
+ permissions: {}
12
+
13
+ env:
14
+ UV_FROZEN: true
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 10
20
+ steps:
21
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
22
+ with:
23
+ persist-credentials: false
24
+ - name: Verify tag matches pyproject version
25
+ run: |
26
+ set -euo pipefail
27
+ TAG="${GITHUB_REF_NAME#v}"
28
+ VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
29
+ [[ "$TAG" == "$VERSION" ]] || { echo "::error::Tag $GITHUB_REF_NAME does not match pyproject version $VERSION"; exit 1; }
30
+ if curl -sf "https://pypi.org/pypi/localml/$VERSION/json" >/dev/null 2>&1; then
31
+ echo "::error::Version $VERSION already exists on PyPI"
32
+ exit 1
33
+ fi
34
+ - uses: ./.github/actions/setup-uv
35
+ - run: uv build
36
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+ if-no-files-found: error
41
+
42
+ publish-pypi:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ timeout-minutes: 5
46
+ environment:
47
+ name: pypi
48
+ url: https://pypi.org/p/localml
49
+ permissions:
50
+ id-token: write
51
+ steps:
52
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
57
+
58
+ github-release:
59
+ needs: publish-pypi
60
+ runs-on: ubuntu-latest
61
+ timeout-minutes: 5
62
+ permissions:
63
+ contents: write
64
+ env:
65
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66
+ GH_REPO: ${{ github.repository }}
67
+ TAG: ${{ github.ref_name }}
68
+ steps:
69
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
70
+ with:
71
+ name: dist
72
+ path: dist/
73
+ - name: Create or update GitHub Release
74
+ run: |
75
+ gh release view "$TAG" >/dev/null 2>&1 || gh release create "$TAG" --generate-notes
76
+ gh release upload "$TAG" --clobber dist/*
@@ -0,0 +1,26 @@
1
+ name: Audit workflows
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [".github/workflows/**", ".github/actions/**"]
7
+ pull_request:
8
+ paths: [".github/workflows/**", ".github/actions/**"]
9
+
10
+ permissions:
11
+ actions: read
12
+ contents: read
13
+
14
+ jobs:
15
+ zizmor:
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 5
18
+ steps:
19
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
20
+ with:
21
+ persist-credentials: false
22
+ - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
23
+ with:
24
+ token: ${{ secrets.GITHUB_TOKEN }}
25
+ advanced-security: false
26
+ annotations: true
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ site/
9
+ .venv/
10
+ venv/
11
+
12
+ # Tooling
13
+ .cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .pytest_cache/
17
+ .coverage
18
+ htmlcov/
19
+
20
+ # Env / secrets
21
+ .env
22
+ ~/.localml/
23
+
24
+ # Local data / volumes
25
+ data/
26
+ *.db
27
+ *.sqlite3
28
+ mlruns/
29
+
30
+ # OS / editor
31
+ .DS_Store
32
+ .idea/
33
+ .vscode/
@@ -0,0 +1,20 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+
10
+ - repo: https://github.com/gitleaks/gitleaks
11
+ rev: v8.30.1
12
+ hooks:
13
+ - id: gitleaks
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: v0.15.14
17
+ hooks:
18
+ - id: ruff-check
19
+ args: [--fix]
20
+ - id: ruff-format
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here, following
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
5
+ [SemVer](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-06-06
10
+
11
+ ### Added
12
+
13
+ - Initial local ML experimentation platform scaffold.
@@ -0,0 +1,45 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ This project uses [`uv`](https://docs.astral.sh/uv/); `uv.lock` is canonical and CI runs
6
+ with `UV_FROZEN=true`.
7
+
8
+ ```sh
9
+ git clone https://github.com/guenp/localml
10
+ cd localml
11
+ uv sync
12
+ pre-commit install
13
+ ```
14
+
15
+ ## Running checks locally
16
+
17
+ ```sh
18
+ uv run pytest
19
+ uv run ruff check
20
+ uv run ruff format --check
21
+ uv run ty check src/
22
+ uv run zensical build
23
+ pre-commit run --all-files
24
+ ```
25
+
26
+ ## Pull request workflow
27
+
28
+ 1. Branch off `main`, make changes, add/update tests.
29
+ 2. Run the checks above.
30
+ 3. Open a PR. PR titles become release notes via `gh release create --generate-notes`,
31
+ so write them imperative and user-facing.
32
+ 4. CI must pass: lint, the test matrix, docs, workflow audit, and `pip-audit`.
33
+
34
+ Reflect user-visible changes in [CHANGELOG.md](CHANGELOG.md) under `[Unreleased]`.
35
+
36
+ ## Releasing
37
+
38
+ Bump `version` in `pyproject.toml`, then push a matching tag:
39
+
40
+ ```sh
41
+ git tag v0.1.0 && git push origin v0.1.0
42
+ ```
43
+
44
+ The release workflow verifies the tag matches the version, builds, publishes to PyPI via
45
+ Trusted Publishing (OIDC), and creates a GitHub Release.
localml-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Guenevere Prawiroatmodjo
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.
localml-0.1.0/Makefile ADDED
@@ -0,0 +1,37 @@
1
+ .PHONY: install lint fmt format-check typecheck docs test up down logs seed reset
2
+
3
+ install:
4
+ uv sync
5
+
6
+ lint:
7
+ uv run ruff check
8
+
9
+ fmt:
10
+ uv run ruff format
11
+
12
+ format-check:
13
+ uv run ruff format --check
14
+
15
+ typecheck:
16
+ uv run ty check src/
17
+
18
+ docs:
19
+ uv run zensical build
20
+
21
+ test:
22
+ uv run pytest
23
+
24
+ up:
25
+ docker compose up -d
26
+
27
+ down:
28
+ docker compose down
29
+
30
+ logs:
31
+ docker compose logs -f
32
+
33
+ seed:
34
+ uv run python scripts/seed.py
35
+
36
+ reset:
37
+ uv run python scripts/reset.py
localml-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: localml
3
+ Version: 0.1.0
4
+ Summary: Local ML experimentation platform demo SDK and control plane
5
+ Project-URL: Homepage, https://github.com/guenp/localml
6
+ Project-URL: Documentation, https://guenp.github.io/localml/
7
+ Project-URL: Repository, https://github.com/guenp/localml
8
+ Project-URL: Changelog, https://github.com/guenp/localml/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/guenp/localml/issues
10
+ Author: Guenevere Prawiroatmodjo
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: local-development,machine-learning,mlops,sdk
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: httpx>=0.27
26
+ Requires-Dist: pydantic>=2.6
27
+ Requires-Dist: typer>=0.12
28
+ Provides-Extra: api
29
+ Requires-Dist: alembic>=1.13; extra == 'api'
30
+ Requires-Dist: boto3>=1.34; extra == 'api'
31
+ Requires-Dist: fastapi>=0.110; extra == 'api'
32
+ Requires-Dist: mlflow>=2.12; extra == 'api'
33
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'api'
34
+ Requires-Dist: redis>=5.0; extra == 'api'
35
+ Requires-Dist: sqlalchemy>=2.0; extra == 'api'
36
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'api'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # localml
40
+
41
+ [![CI](https://github.com/guenp/localml/actions/workflows/ci.yml/badge.svg)](https://github.com/guenp/localml/actions/workflows/ci.yml)
42
+
43
+ A **local ML experimentation platform demo** that runs entirely on an Apple Silicon
44
+ workstation. It demonstrates the core architecture of a production ML platform at local
45
+ scale: a Python SDK, framework adapters, experiment tracking, a model registry, artifact
46
+ storage, evaluation jobs, and local model serving.
47
+
48
+ > Status: **early scaffold.** Most components are stubs with coherent interfaces. See
49
+ > [`ROADMAP.md`](./ROADMAP.md) for what's planned and [`docs/design.md`](./docs/design.md)
50
+ > for the full software design document.
51
+
52
+ ## What's here
53
+
54
+ ```
55
+ localml/
56
+ ├── src/localml/ # Python SDK (`import localml as ml`)
57
+ │ ├── adapters/ # torch / jax / mlx / huggingface framework adapters
58
+ │ ├── client.py # HTTPX client for the control plane
59
+ │ ├── config.py # ~/.localml/config.toml handling
60
+ │ ├── exceptions.py # typed SDK errors
61
+ │ ├── run.py # run context manager
62
+ │ ├── types.py # Run / ModelVersion / EvaluationJob / Deployment
63
+ │ └── cli.py # Typer CLI
64
+ ├── services/
65
+ │ ├── api/ # FastAPI control plane
66
+ │ ├── worker/ # Redis-backed evaluation worker
67
+ │ └── mlflow/ # MLflow tracking + registry image
68
+ ├── docs/ # Zensical documentation site and design document
69
+ ├── docker-compose.yml # Local stack: api, worker, postgres, redis, minio, mlflow, serving
70
+ └── tests/
71
+ ```
72
+
73
+ ## Architecture (at a glance)
74
+
75
+ ```mermaid
76
+ flowchart LR
77
+ User[SDK / CLI / Notebook] --> API[FastAPI control plane]
78
+ API --> MLflow[MLflow<br/>tracking + registry]
79
+ API --> DB[(Postgres<br/>metadata)]
80
+ API --> Store[(MinIO<br/>artifacts)]
81
+ API --> Queue[Redis<br/>job queue]
82
+ API --> Serving[Local inference<br/>Ollama / MLX]
83
+ Queue --> Worker[Worker]
84
+ Worker --> Store
85
+ Worker --> DB
86
+ ```
87
+
88
+ The control plane (Postgres) is the source of truth for platform metadata. MLflow holds
89
+ experiment tracking state, MinIO holds artifacts, and Redis holds transient job state.
90
+
91
+ ## Quick start
92
+
93
+ ### 1. Bring up the stack
94
+
95
+ ```bash
96
+ cp .env.example .env
97
+ docker compose up -d
98
+ ```
99
+
100
+ This starts Postgres, Redis, MinIO, MLflow, the FastAPI control plane, the worker, and a
101
+ local serving runtime.
102
+
103
+ | Service | URL |
104
+ | ------------- | ----------------------- |
105
+ | Control plane | http://localhost:8000 |
106
+ | API docs | http://localhost:8000/docs |
107
+ | MLflow UI | http://localhost:5000 |
108
+ | MinIO console | http://localhost:9001 |
109
+
110
+ ### 2. Install the SDK
111
+
112
+ ```bash
113
+ uv sync # or: pip install -e .
114
+ ```
115
+
116
+ ### 3. Run the example workflow
117
+
118
+ ```python
119
+ import localml as ml
120
+
121
+ ml.configure(api_url="http://localhost:8000", token="local-dev-token")
122
+
123
+ with ml.start_run(project="local-demo", config={"model": "tiny-llm"}) as run:
124
+ ml.log_params({"batch_size": 4, "quantization": "4bit"})
125
+ ml.log_metrics({"baseline_accuracy": 0.82})
126
+
127
+ version = ml.huggingface.log_pretrained(
128
+ name="tiny-assistant",
129
+ model_dir="./models/tiny-assistant",
130
+ metadata={"task": "chat", "runtime": "mlx"},
131
+ )
132
+
133
+ eval_job = ml.evaluate(
134
+ model=version,
135
+ dataset="datasets/eval.jsonl",
136
+ metrics=["exact_match", "latency_p95"],
137
+ )
138
+ eval_job.wait()
139
+
140
+ deployment = ml.deploy(model=version, target="local")
141
+ print(deployment.predict({"prompt": "Explain model registries simply."}))
142
+ ```
143
+
144
+ ### CLI
145
+
146
+ ```bash
147
+ localml --help
148
+ localml projects list
149
+ localml runs get <run_id>
150
+ ```
151
+
152
+ ## Development
153
+
154
+ Uses [`uv`](https://docs.astral.sh/uv/) for Python and dependency management; `uv.lock` is
155
+ canonical and CI runs with `UV_FROZEN=true`.
156
+
157
+ ```bash
158
+ uv sync
159
+ pre-commit install
160
+
161
+ uv run pytest # tests with coverage
162
+ uv run ruff check # lint
163
+ uv run ruff format --check # format check
164
+ uv run ty check src/ # type check
165
+ uv run zensical serve # live-preview the docs
166
+ ```
167
+
168
+ Docs are authored in `docs/` and built with [Zensical](https://zensical.org);
169
+ `docs.yml` deploys them to GitHub Pages on every push to `main`.
170
+
171
+ ## Model lifecycle
172
+
173
+ ```
174
+ created → candidate → staging → production → deprecated → archived
175
+ ↘ failed (from candidate/staging) ↘ archived (terminal)
176
+ ```
177
+
178
+ ## License
179
+
180
+ MIT. See [LICENSE](LICENSE).