Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
93 changes: 93 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Linux — the primary gate, and the cheap one. Full Python matrix, browser tests
# included, plus the documented-numbers check.
#
# The package is pure Python (one py3-none-any wheel), so nothing is compiled here;
# what the three OS workflows actually prove is that the *runtime* behaves the same
# everywhere — path handling, SQLite file locking, threading, and the browser render.
name: Linux

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
# A superseded PR run is wasted runner time. A push to main is not cancelled:
# when CI goes red we want to know which commit did it.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
# Named explicitly so the check context is unambiguous. Three workflows with a
# job called "test" produce three checks called "test", and branch protection
# cannot then require a specific one.
name: Linux ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

# Browsers are ~100 MB and never change between runs; downloading them on
# every job is the single most wasteful thing this workflow could do.
# The key includes the Playwright version, so a bump invalidates it.
- name: Playwright version
id: pw
run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }}

- name: Install browser
# --only-shell is the headless shell alone: about a third of the download,
# and the tests never open a headed browser.
run: python -m playwright install chromium --only-shell --with-deps
if: steps.pw-cache.outputs.cache-hit != 'true'

- name: Install browser dependencies
# System libraries are not in the cached browser directory.
run: python -m playwright install-deps chromium
if: steps.pw-cache.outputs.cache-hit == 'true'

- name: Lint
run: python -m ruff check .

- name: Test
run: python -m pytest tests/ -q -m "not ollama"

# One gate: lint, tests and the documented numbers pass together. Keeping this
# as a separate optional target is how a README drifts from what the code does.
- name: Check documented numbers
run: python scripts/check_numbers.py

- name: Build the wheel
run: |
pip install build
python -m build

- name: Wheel installs and runs clean
run: |
python -m venv /tmp/fresh
/tmp/fresh/bin/pip install --quiet dist/*.whl
/tmp/fresh/bin/mpe-lkg --version
/tmp/fresh/bin/python -c "import mpe_lkg; a = mpe_lkg.create_app(); assert a.test_client().get('/').status_code == 200"
60 changes: 60 additions & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# macOS — one Python version, full suite including the browser render tests.
#
# A single version on purpose: macOS runners cost ten times a Linux minute, and the
# Python-version axis is already covered on Linux. What this run is actually for is
# the platform axis — arm64, a different SQLite build, a different browser build.
name: macOS

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
# Explicit, so branch protection can require this exact context.
name: macOS
runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Playwright version
id: pw
run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@v4
with:
path: ~/Library/Caches/ms-playwright
key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }}

- name: Install browser
run: python -m playwright install chromium --only-shell
if: steps.pw-cache.outputs.cache-hit != 'true'

- name: Lint
run: python -m ruff check .

- name: Test
run: python -m pytest tests/ -q -m "not ollama"

- name: Check documented numbers
run: python scripts/check_numbers.py
83 changes: 83 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Publish to PyPI on a version tag.
#
# The package is pure Python, so there is exactly one artefact to build -- a
# py3-none-any wheel that serves Linux, macOS and Windows on every supported
# interpreter. No cibuildwheel matrix, no cross-compilation, nothing to sign per
# platform.
#
# Publishing is gated on the three OS workflows being green for the same commit;
# a tag is not a reason to skip the tests.
name: PyPI

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
dry_run:
description: "Build and check only, do not upload"
type: boolean
default: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]" build twine

- name: Lint
run: python -m ruff check .

# Browser tests are skipped here: the OS workflows already ran them for this
# commit. What must not be skipped is that the code imports and behaves.
- name: Test
run: python -m pytest tests/ -q -m "not ollama" --ignore=tests/test_render.py

- name: Build
run: python -m build

- name: Check metadata
run: python -m twine check dist/*

- name: The tag must match the version in pyproject
if: startsWith(github.ref, 'refs/tags/v')
run: |
TAG="${GITHUB_REF_NAME#v}"
PKG=$(python -c "import tomllib,pathlib;print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
echo "tag=$TAG pyproject=$PKG"
test "$TAG" = "$PKG" || { echo "::error::tag $TAG does not match version $PKG"; exit 1; }

- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') || inputs.dry_run == false
environment: pypi
permissions:
id-token: write # for trusted publishing, once it is configured
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- uses: pypa/gh-action-pypi-publish@release/v1
with:
# Remove this once Trusted Publishing is set up for the project on PyPI,
# which removes the need to hold a token at all.
password: ${{ secrets.PYPI_API_TOKEN }}
65 changes: 65 additions & 0 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Windows — the platform most likely to find a real bug in this codebase.
#
# Not because of the language, but because of the file semantics: an open SQLite
# handle cannot be deleted on Windows, path separators differ, and the temporary
# directories pytest hands out are cleaned up differently. Those are exactly the
# places a store-backed web app breaks.
name: Windows

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
# Explicit, so branch protection can require this exact context.
name: Windows
runs-on: windows-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Playwright version
id: pw
shell: bash
run: echo "version=$(python -c 'import playwright; print(playwright.__version__)')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@v4
with:
path: ~\AppData\Local\ms-playwright
key: pw-${{ runner.os }}-${{ steps.pw.outputs.version }}

- name: Install browser
run: python -m playwright install chromium --only-shell
if: steps.pw-cache.outputs.cache-hit != 'true'

- name: Lint
run: python -m ruff check .

- name: Test
run: python -m pytest tests/ -q -m "not ollama"

- name: Check documented numbers
run: python scripts/check_numbers.py

- name: Console script works
run: mpe-lkg --version
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
__pycache__/
*.py[cod]
.venv/
venv/
.pytest_cache/
.ruff_cache/

# Runtime artefacts written into the working directory
embeddings.db
embeddings.ann

# docs/claims/*.json is deliberately NOT ignored: those files are the record of
# what was measured, and scripts/check_numbers.py verifies the README against them.

.DS_Store

# Build artefacts
dist/
build/
*.egg-info/
46 changes: 46 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
PY := .venv/bin/python

.PHONY: all venv lint test test-ollama measure sweep bench check-numbers run clean

# One gate. Lint, tests and the documented numbers pass together or the build is
# not green -- keeping the numbers in a separate optional target is how a README
# drifts away from what the code actually does.
all: lint test check-numbers

# pyproject.toml is the single source of truth for dependencies; there is no
# requirements.txt to drift out of sync with it.
venv:
uv venv --python 3.12 .venv
uv pip install --python $(PY) -e ".[dev]"
$(PY) -m playwright install chromium --only-shell

lint:
$(PY) -m ruff check .

test:
$(PY) -m pytest tests/ -q -m "not ollama"

# Requires a running Ollama with the models named in the README.
test-ollama:
$(PY) -m pytest tests/ -q -m ollama

# Regenerates docs/claims/*.json from a live Ollama.
measure:
$(PY) scripts/measure.py

# How well does each layer of a local model separate topics? Needs torch.
sweep:
$(PY) scripts/layer_sweep.py

# Exact scan versus an approximate index, at several store sizes.
bench:
$(PY) scripts/bench_search.py

check-numbers:
$(PY) scripts/check_numbers.py

run:
$(PY) app.py

clean:
rm -rf .pytest_cache .ruff_cache __pycache__ tests/__pycache__ embeddings.db embeddings.ann
Loading
Loading