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
100 changes: 100 additions & 0 deletions .github/workflows/bump-laya.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Keep Laya current (docs/laya.md): when NandhaKishorM/laya main or the
# convaiinnovations/laya model repo on Hugging Face moves, open a PR that moves
# third_party/laya and config/laya.json's revision together. Before merging,
# run scripts/fetch-laya.sh and the Laya tests against the new revision.
#
# Uses the secret HRX_BUMP_TOKEN (Contents and Pull requests read/write on
# 1bit-MONSTER/engine): a PR opened with the default GITHUB_TOKEN would not
# start CI.
name: bump-laya

on:
schedule:
- cron: "53 7 * * *"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: bump-laya
cancel-in-progress: false

jobs:
bump:
runs-on: ubuntu-latest
steps:
- name: Require the token
env:
HRX_BUMP_TOKEN: ${{ secrets.HRX_BUMP_TOKEN }}
run: |
if [ -z "$HRX_BUMP_TOKEN" ]; then
echo "::error::secret HRX_BUMP_TOKEN is not set (see the header of this workflow)"
exit 1
fi

- uses: actions/checkout@v4
with:
token: ${{ secrets.HRX_BUMP_TOKEN }}

- name: Compare with upstream
id: pins
run: |
set -euo pipefail
src=$(git ls-remote https://github.com/NandhaKishorM/laya.git refs/heads/main | awk '{print $1}')
repo=$(python3 -c 'import json; print(json.load(open("config/laya.json"))["repo"])')
rev=$(curl -fsSL "https://huggingface.co/api/models/${repo}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])')
our_src=$(git ls-tree HEAD third_party/laya | awk '{print $3}')
our_rev=$(python3 -c 'import json; print(json.load(open("config/laya.json"))["revision"])')
echo "source $src (ours $our_src), model $rev (ours $our_rev)"
{
echo "src=$src"; echo "rev=$rev"; echo "our_src=$our_src"; echo "our_rev=$our_rev"
if [ "$src" = "$our_src" ] && [ "$rev" = "$our_rev" ]; then echo "changed=false"; else echo "changed=true"; fi
} >> "$GITHUB_OUTPUT"

- name: Open the bump PR
if: steps.pins.outputs.changed == 'true'
env:
GH_TOKEN: ${{ secrets.HRX_BUMP_TOKEN }}
SRC: ${{ steps.pins.outputs.src }}
REV: ${{ steps.pins.outputs.rev }}
OUR_SRC: ${{ steps.pins.outputs.our_src }}
OUR_REV: ${{ steps.pins.outputs.our_rev }}
run: |
set -euo pipefail
branch="bump-laya/${SRC:0:8}-${REV:0:8}"
if git ls-remote --exit-code origin "refs/heads/$branch" > /dev/null; then
echo "$branch already exists"; exit 0
fi
git config user.name "laya-bump"
git config user.email "laya-bump@users.noreply.github.com"
git switch -c "$branch"
git update-index --cacheinfo "160000,$SRC,third_party/laya"
python3 - "$REV" <<'PY'
import json, sys
p = json.load(open("config/laya.json")); p["revision"] = sys.argv[1]
open("config/laya.json", "w").write(json.dumps(p, indent=2) + "\n")
PY
git add config/laya.json
git commit -q -m "Bump Laya: source ${SRC:0:12}, model ${REV:0:12}"
git push -q origin "$branch"
gh pr create --base main --head "$branch" \
--title "Bump Laya: source ${SRC:0:8}, model ${REV:0:8}" \
--body "third_party/laya: \`${OUR_SRC:0:12}\` -> \`${SRC:0:12}\` (NandhaKishorM/laya main). config/laya.json revision: \`${OUR_REV:0:12}\` -> \`${REV:0:12}\` (convaiinnovations/laya).

Before merging: scripts/fetch-laya.sh <dir> (verifies every file) and the Laya tests."
8 changes: 8 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@
path = third_party/linux
url = https://github.com/torvalds/linux.git
shallow = true
# Laya (docs/laya.md): the step-4 router's upstream source, NandhaKishorM/laya.
# The model checkpoints are pinned by Hugging Face revision in config/laya.json
# and fetched by scripts/fetch-laya.sh; .github/workflows/bump-laya.yml keeps both current.
[submodule "third_party/laya"]
path = third_party/laya
url = https://github.com/NandhaKishorM/laya.git
branch = main
shallow = true
5 changes: 5 additions & 0 deletions config/laya.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"repo": "convaiinnovations/laya",
"revision": "aa8c91ca088ec597df95a0d1c76b3063cb2ae5e8",
"note": "Laya's three checkpoints (root, multilingual/, typed-decisions/) at one Hugging Face revision; scripts/fetch-laya.sh downloads exactly this revision and checks every file's hash against the Hub's file list."
}
2 changes: 1 addition & 1 deletion docs/PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Each step below is one PR (or a short series) that builds and runs on Strix Halo
| 2 | **HRX with Vulkan.** llama.cpp with `GGML_HRX=ON` and `GGML_VULKAN=ON` in one build, on AMD's tested pair | `1bit-MONSTER/llama.cpp` `1bit/hrx-vulkan` (AMD's `hrx-graph-develop-v2`) + `ROCm/hrx-system`, pinned from `ROCm/ggml-staging-automation` | **landed** ([docs/hrx.md](hrx.md)): Lemonade serves the same checkpoint on `Vulkan0` and `HRX0`; kept current by `bump-hrx.yml` |
| 3 | **NPU engine.** Full ELFs only, and the open 16-tile layer kernel built from source | `engine/npu` (`npu_engine_universal.cpp`, `I8Ctx::init_elf`); ELF dispatch table on `backup/iso-build-elf-native-2026-09-22`; kernel on `bench/fastlane-16tile-corrections-2026-09-22` | **3a–3c landed** ([docs/npu.md](npu.md)). 3a (#8): full ELFs generated in C++, reproducing all 5,153 captured contexts. 3b/3c (#9): the lane runtime (logits bit-identical to the reference lane, 24/24 steps, 11.0 ms/token), tokenizer, `1bit unified`, and Lemonade serving Qwen3-0.6B on the NPU through `onebit`. The XDNA driver and XRT are pinned upstream and built privately (#12). **Open:** the layer kernel and lm-head artifacts are not yet built from source (npu.md, "Open") |
| + | **Linux kernel.** The kernel that provides `amdxdna` and `amdgpu`, pinned to upstream | `torvalds/linux` release tags; config from the Strix Halo kernel of 2026-09-23 | **pinned** ([docs/kernel.md](kernel.md)): v7.3-rc4 builds into Debian packages with `amdxdna` in-tree; kept current by `bump-linux.yml`. Installing it on Strix Halo is a separate, deliberate step |
| 4 | **Laya router.** A non-autoregressive scorer that picks where each request runs | `src/laya_scorer.cpp`, `include/laya_scorer.h` on `backup/laya-and-results-2026-09-22`; model at `~/models/laya` | matches its Python reference; routes requests |
| 4 | **Laya router.** A non-autoregressive scorer that picks where each request runs | `src/laya_scorer.cpp`, `include/laya_scorer.h` on `backup/laya-and-results-2026-09-22`; model at `~/models/laya` | **pinned** ([docs/laya.md](laya.md)): source `NandhaKishorM/laya` + the three HF checkpoints at one revision, hash-verified fetch, `bump-laya.yml`. Next: the C++ scorer, gated against the Python reference; then routing |
| 5 | **Every HF model, kept current.** The architecture registry (569 tokens mapping 2,030 HF arch strings) and the daily HF census that finds new architectures and proposes mappings | `src/model_registry*.cpp`, `Testing/census_*.py` and `.json`, `.github/workflows/census-{watch,sweep,autopr}.yml` | the census runs daily in CI; docs report *mapped* and *run and checked* counts separately |
| 6 | **ZINC (NVIDIA and more).** Upstream `zolotukhin/zinc`, a Zig GGUF engine with Vulkan, ROCm, CUDA and Metal backends; its CUDA backend reaches NVIDIA GPUs (Ada `sm_89`, Blackwell `sm_120`) | not in 1bit-MONSTER; pinned from upstream `main` | **pinned** ([docs/zinc.md](zinc.md)): `scripts/build-zinc.sh` builds it privately; the Vulkan build gives 12095 (" Paris") at 295 tok/s on Strix Halo; the CUDA build answers " Paris." at 167–173 tok/s on an RTX 5090 (Qwen3.5-9B); kept current by `bump-zinc.yml`. `1bit lemonade` serves it as the `zinc` recipe (`-DONEBIT_ZINC=ON`, e2e passes) |

Expand Down
49 changes: 49 additions & 0 deletions docs/laya.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!--
Copyright 2026 bong-water-water-bong
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Laya

Step 4 of the port (docs/PORTING.md): Laya picks where each request runs.
[Laya](https://github.com/NandhaKishorM/laya) (Apache-2.0) is a
non-autoregressive decision model: a ModernBERT-style encoder plus an RLCD
decision head. It answers typed questions (`choice`, `score`, `noul`) about a
request in one forward pass, with calibrated confidence. It has three
checkpoints and a router that picks between them.

## The pin

| | Pin | Kept current by |
|---|---|---|
| Source | `third_party/laya` = `NandhaKishorM/laya` main **`1e28ac20`** | `bump-laya.yml` |
| Checkpoints | `config/laya.json` = Hugging Face `convaiinnovations/laya` at revision **`aa8c91ca`**: root (842 MB), `multilingual/` (644 MB, 34 MB tokenizer), `typed-decisions/` (842 MB) | `bump-laya.yml` (moves the source and the revision together) |

```sh
scripts/fetch-laya.sh ~/models/laya-pinned
```

`fetch-laya.sh` downloads exactly the pinned revision and checks every file
against the Hub's list: sha256 for the LFS weights, the git blob id for the rest.
Files already present and correct are not fetched again. Images and eval plots
are skipped.

Verified 2026-09-23 on Strix Halo: 20 files, 2.3 GB, all hashes match.

## Next

The C++ scorer (1bit-MONSTER `src/laya_scorer.cpp`, on
`backup/laya-and-results-2026-09-22`) is ported next. It is gated against
the Python reference in `third_party/laya` on the pinned checkpoints, then
wired into the router that picks NPU, HRX, Vulkan or ZINC per request.
63 changes: 63 additions & 0 deletions scripts/fetch-laya.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# fetch-laya.sh <dir>
#
# Downloads the Laya checkpoints pinned in config/laya.json (one Hugging Face
# revision holding all three: root, multilingual/, typed-decisions/) into
# <dir>, and checks every file against the Hub's list for that revision:
# sha256 for LFS files (the weights), the git blob id for the small ones.
# Images and the eval plots are skipped. Files already present and correct are
# not downloaded again.
set -euo pipefail
dir=${1:?usage: fetch-laya.sh <dir>}
root=$(cd "$(dirname "$0")/.." && pwd)
mkdir -p "$dir"
python3 - "$root/config/laya.json" "$dir" <<'PYEOF'
import hashlib, json, os, sys, urllib.request

pin = json.load(open(sys.argv[1]))
repo, rev, out = pin["repo"], pin["revision"], sys.argv[2]
api = f"https://huggingface.co/api/models/{repo}/tree/{rev}?recursive=true"
files = [f for f in json.load(urllib.request.urlopen(api))
if f["type"] == "file" and not f["path"].startswith(("assets/", "eval/"))]

def digest(path, lfs):
data_h = hashlib.sha256() if lfs else hashlib.sha1()
if not lfs: # git blob id = sha1("blob <size>\0" + contents)
data_h.update(f"blob {os.path.getsize(path)}\0".encode())
with open(path, "rb") as fh:
for chunk in iter(lambda: fh.read(1 << 20), b""):
data_h.update(chunk)
return data_h.hexdigest()

for f in files:
lfs = f.get("lfs")
want = lfs["oid"] if lfs else f["oid"]
dst = os.path.join(out, f["path"])
if os.path.exists(dst) and digest(dst, bool(lfs)) == want:
continue
os.makedirs(os.path.dirname(dst) or ".", exist_ok=True)
url = f"https://huggingface.co/{repo}/resolve/{rev}/{f['path']}"
urllib.request.urlretrieve(url, dst + ".part")
got = digest(dst + ".part", bool(lfs))
if got != want:
os.remove(dst + ".part")
sys.exit(f"{f['path']}: hash {got} != pinned {want}")
os.replace(dst + ".part", dst)
print(f"fetched {f['path']} ({f['size']} B)")
print(f"Laya {repo}@{rev[:12]}: {len(files)} files verified in {out}")
PYEOF
1 change: 1 addition & 0 deletions third_party/laya
Submodule laya added at 1e28ac
Loading