Wire LLM-extracted kg triples into the TripleStore (#840, contract) #2372
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| branches: [main, master] | |
| jobs: | |
| docs-check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 | |
| with: | |
| python-version: "3.11" | |
| # No pip install on purpose: the generator parses the source with ast | |
| # rather than importing mnemosyne, so this job stays dependency-free. | |
| # If that ever changes, this job needs the package installed. | |
| - name: Report the surface the code declares | |
| run: python3 scripts/generate-docs.py --check 2>&1 | |
| # Verifies the committed docs/api/*.mdx against freshly generated | |
| # output, in-process. The previous implementation resolved a sibling | |
| # repo that is never checked out here and exited 0, so this gate | |
| # passed unconditionally and checked nothing. | |
| - name: Verify generated docs are up to date | |
| run: python3 scripts/verify-docs.py 2>&1 | |
| lint: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| # Fetch enough history for `git diff origin/main...HEAD` to | |
| # work on PR builds. Default depth=1 breaks this — `Actions | |
| # checkout@4+` exposes `fetch-depth` explicitly. | |
| fetch-depth: 0 | |
| # Persist the auto-issued GITHUB_TOKEN so the F-rules step | |
| # can `git fetch origin base-ref` on fork PRs (the default | |
| # `persist-credentials: false` would block that with a 403). | |
| persist-credentials: true | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 | |
| with: | |
| python-version: "3.11" | |
| - name: Install Ruff | |
| run: python -m pip install ruff==0.15.22 | |
| # ------------------------------------------------------------------ | |
| # Step A: project-wide fatal gate. | |
| # | |
| # Pure syntax / undefined-name / unparsable cases. These are the | |
| # ones that have always been enforced and would ship broken code | |
| # otherwise — they stay fatal across the whole tree. | |
| # ------------------------------------------------------------------ | |
| - name: Check fatal Python errors | |
| run: ruff check --select E9,F63,F7,F82 mnemosyne tests integrations/hermes/src integrations/hermes/tests hermes_memory_provider | |
| # ------------------------------------------------------------------ | |
| # Step B: PR-scoped F/RUF022 sweep over CHANGED files only. | |
| # | |
| # Why scoped: turning the F-ruleset on tree-wide would surface | |
| # ~423 legacy violations on current `main`, fail every open PR, | |
| # and reintroduce the original problem this job was supposed to | |
| # prevent (any push can stop the project). The family we're | |
| # adding — F401/F841/F541/RUF022 — is cheap to write, so we | |
| # enforce it on new code and let historical violations get | |
| # cleaned up via dedicated PRs. | |
| # | |
| # Behavior: | |
| # - pull_request: list .py files changed vs the PR base, run | |
| # ruff with the broader rubric. Fails if any DIFF file has | |
| # new F/RUF022 violations. | |
| # - push to main: skip. The F-rules live in PR-only land until | |
| # someone files the cleanup PR that brings main itself under | |
| # the same rubric. | |
| # | |
| # See PR #483 followup for context. | |
| # ------------------------------------------------------------------ | |
| - name: Check F-rules on changed files (PRs only) | |
| if: github.event_name == 'pull_request' | |
| env: | |
| # Pin the PR base ref from the workflow event into the shell | |
| # environment so we don't have to splice it through ${{ }} | |
| # inside the bash script (which interacts poorly with quoting | |
| # and would let a hostile base.ref break out of the string). | |
| PR_BASE_REF: ${{ github.event.pull_request.base.ref }} | |
| run: | | |
| set -euo pipefail | |
| # Fetch the base branch explicitly (shallow — we only need | |
| # the diff walker) and resolve BASE_REF after the fetch so | |
| # `origin/${PR_BASE_REF}` exists locally. | |
| git fetch --depth=1 origin "${PR_BASE_REF}" | |
| BASE_REF="origin/${PR_BASE_REF}" | |
| # NUL-delimited so paths with spaces / newlines survive. | |
| # No `|| true` — if the diff walker itself breaks, the step | |
| # should fail loudly rather than silently skipping lint. | |
| # | |
| # Why a temp file instead of a process substitution feeding | |
| # `mapfile`: mapfile reports the exit code of the array | |
| # *assignment*, not the substituted command, so a failing | |
| # `git diff` would leave `CHANGED` empty and exit 0 — silently | |
| # bypassing the PR lint gate. Writing to a file first lets | |
| # `set -e` observe and propagate the failure (the script | |
| # aborts before `mapfile` runs). | |
| changed_files="$(mktemp)" | |
| trap 'rm -f "${changed_files}"' EXIT | |
| git diff --name-only --diff-filter=ACMRT -z \ | |
| "${BASE_REF}...HEAD" -- '*.py' > "${changed_files}" | |
| mapfile -d '' -t CHANGED < "${changed_files}" | |
| if [ "${#CHANGED[@]}" -eq 0 ]; then | |
| echo "No Python files changed — skipping F-rules sweep." | |
| exit 0 | |
| fi | |
| echo "Python files changed on this PR:" | |
| printf ' - %s\n' "${CHANGED[@]}" | |
| # `--` so a path like `--something.py` is treated as a | |
| # positional argument rather than a ruff option. | |
| ruff check --select F,RUF022 -- "${CHANGED[@]}" | |
| test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13"] | |
| env: | |
| # Force lexical-only retrieval in CI. Sleep / consolidation / | |
| # recall tests exercise the no-embeddings degradation path, which | |
| # is the same code path the production system takes when the | |
| # model download fails or the user opts out. This eliminates the | |
| # Hugging Face 429 cascade that previously caused 48+ failures | |
| # on the test (3.11) job. Set MNEMOSYNE_EMBEDDINGS_FORCE_DOWNLOAD=1 | |
| # in a follow-up job if you want to exercise the real model path. | |
| MNEMOSYNE_NO_EMBEDDINGS: "1" | |
| # Cache dir for the fastembed model (only used when embeddings | |
| # are enabled, but pre-create it so the cache step has a stable | |
| # path to restore from). | |
| MNEMOSYNE_FASTEMBED_CACHE_DIR: ${{ github.workspace }}/.cache/fastembed | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Cache embedding model | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: ${{ env.MNEMOSYNE_FASTEMBED_CACHE_DIR }} | |
| key: fastembed-bge-small-en-v1.5-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| fastembed-bge-small-en-v1.5-${{ matrix.python-version }}- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[embeddings,mcp,sync,test]" | |
| pip install -e ./integrations/hermes | |
| - name: Pre-download embedding model | |
| # Only runs when the cache missed and embeddings are enabled. | |
| # The env override above disables embeddings, so this is a no-op | |
| # for the default job. To run the real embedding path in CI, | |
| # unset MNEMOSYNE_NO_EMBEDDINGS in a follow-up workflow and | |
| # this step will populate the cache on first run. | |
| if: env.MNEMOSYNE_NO_EMBEDDINGS != '1' | |
| run: | | |
| mkdir -p "$MNEMOSYNE_FASTEMBED_CACHE_DIR" | |
| python -c " | |
| from fastembed import TextEmbedding | |
| TextEmbedding( | |
| model_name='BAAI/bge-small-en-v1.5', | |
| cache_dir='$MNEMOSYNE_FASTEMBED_CACHE_DIR', | |
| ) | |
| print('embedding model cached') | |
| " | |
| - name: Run core tests | |
| run: pytest tests/ -v --tb=short | |
| - name: Run Hermes integration tests | |
| run: pytest integrations/hermes/tests/ -v --tb=short | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 | |
| with: | |
| python-version: "3.11" | |
| - name: Install build dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Build package | |
| run: python -m build | |
| - name: Check package with twine | |
| run: twine check dist/* |