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.
- localml-0.1.0/.env.example +26 -0
- localml-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
- localml-0.1.0/.github/actions/setup-uv/action.yml +20 -0
- localml-0.1.0/.github/pull_request_template.md +10 -0
- localml-0.1.0/.github/workflows/ci.yml +62 -0
- localml-0.1.0/.github/workflows/docs.yml +60 -0
- localml-0.1.0/.github/workflows/release.yml +76 -0
- localml-0.1.0/.github/workflows/zizmor.yml +26 -0
- localml-0.1.0/.gitignore +33 -0
- localml-0.1.0/.pre-commit-config.yaml +20 -0
- localml-0.1.0/.python-version +1 -0
- localml-0.1.0/CHANGELOG.md +13 -0
- localml-0.1.0/CONTRIBUTING.md +45 -0
- localml-0.1.0/LICENSE +21 -0
- localml-0.1.0/Makefile +37 -0
- localml-0.1.0/PKG-INFO +180 -0
- localml-0.1.0/README.md +142 -0
- localml-0.1.0/ROADMAP.md +142 -0
- localml-0.1.0/SECURITY.md +6 -0
- localml-0.1.0/docker-compose.yml +65 -0
- localml-0.1.0/docs/design.md +405 -0
- localml-0.1.0/docs/getting-started.md +35 -0
- localml-0.1.0/docs/index.md +43 -0
- localml-0.1.0/docs/reference/api.md +22 -0
- localml-0.1.0/docs/reference/index.md +3 -0
- localml-0.1.0/examples/quickstart.py +70 -0
- localml-0.1.0/pyproject.toml +116 -0
- localml-0.1.0/scripts/reset.py +22 -0
- localml-0.1.0/scripts/seed.py +26 -0
- localml-0.1.0/services/api/Dockerfile +12 -0
- localml-0.1.0/services/api/app/__init__.py +1 -0
- localml-0.1.0/services/api/app/auth.py +22 -0
- localml-0.1.0/services/api/app/config.py +23 -0
- localml-0.1.0/services/api/app/db.py +175 -0
- localml-0.1.0/services/api/app/lifecycle.py +46 -0
- localml-0.1.0/services/api/app/main.py +35 -0
- localml-0.1.0/services/api/app/queue.py +38 -0
- localml-0.1.0/services/api/app/routers/__init__.py +1 -0
- localml-0.1.0/services/api/app/routers/deployments.py +78 -0
- localml-0.1.0/services/api/app/routers/evaluations.py +57 -0
- localml-0.1.0/services/api/app/routers/models.py +77 -0
- localml-0.1.0/services/api/app/routers/projects.py +31 -0
- localml-0.1.0/services/api/app/routers/runs.py +80 -0
- localml-0.1.0/services/api/app/schemas.py +105 -0
- localml-0.1.0/services/api/app/store.py +95 -0
- localml-0.1.0/services/api/requirements.txt +9 -0
- localml-0.1.0/services/mlflow/Dockerfile +15 -0
- localml-0.1.0/services/worker/Dockerfile +10 -0
- localml-0.1.0/services/worker/evaluator.py +35 -0
- localml-0.1.0/services/worker/requirements.txt +5 -0
- localml-0.1.0/services/worker/worker.py +69 -0
- localml-0.1.0/src/localml/__init__.py +62 -0
- localml-0.1.0/src/localml/_state.py +31 -0
- localml-0.1.0/src/localml/adapters/__init__.py +6 -0
- localml-0.1.0/src/localml/adapters/base.py +54 -0
- localml-0.1.0/src/localml/cli.py +62 -0
- localml-0.1.0/src/localml/client.py +208 -0
- localml-0.1.0/src/localml/config.py +97 -0
- localml-0.1.0/src/localml/exceptions.py +35 -0
- localml-0.1.0/src/localml/huggingface.py +33 -0
- localml-0.1.0/src/localml/jax.py +48 -0
- localml-0.1.0/src/localml/mlx.py +32 -0
- localml-0.1.0/src/localml/ops.py +75 -0
- localml-0.1.0/src/localml/py.typed +0 -0
- localml-0.1.0/src/localml/run.py +39 -0
- localml-0.1.0/src/localml/torch.py +44 -0
- localml-0.1.0/src/localml/types.py +98 -0
- localml-0.1.0/tests/conftest.py +33 -0
- localml-0.1.0/tests/test_api.py +80 -0
- localml-0.1.0/tests/test_lifecycle.py +37 -0
- localml-0.1.0/tests/test_sdk.py +69 -0
- localml-0.1.0/uv.lock +3557 -0
- 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,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
|
localml-0.1.0/.gitignore
ADDED
|
@@ -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
|
+
[](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).
|