Repository navigation
Merge pull request #2581 from JustVugg/fix/ui-agents-windows-host #4403
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
| # CI: run the repo's own dependency-free gate (`make check` = clean + portable | |
| # CPU build + C unit suites + Python stdlib tests) on the three claimed | |
| # platforms. No model downloads, no CUDA, no external deps — by design (#140). | |
| name: check | |
| on: | |
| push: | |
| branches: [main, dev] | |
| pull_request: | |
| branches: [main, dev] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: check-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHONUTF8: '1' | |
| jobs: | |
| linux: | |
| name: Linux | |
| runs-on: ubuntu-latest | |
| # make check built serially and took 23 minutes on 2026-10-17, then past 25 with two | |
| # more engines' GGUF code: the builds now use every core (make test runs the C and | |
| # Python suites one after the other, so only the compiling is parallel). | |
| timeout-minutes: 35 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: make check | |
| run: make -C c -j"$(nproc)" check | |
| windows: | |
| # The job that would have caught #68/#137 pre-merge: native MinGW-w64 | |
| # (MSYS2/UCRT64), the exact toolchain the README's Windows port targets. | |
| name: Windows UCRT64 | |
| runs-on: windows-latest | |
| # Measured, not guessed: this job now runs 19-21 minutes, so a 20-minute | |
| # limit decided the result by runner luck. Three consecutive runs on | |
| # 2026-08-27 took 19m21s (passed), 20m18s and 20m32s (both cancelled at the | |
| # limit and reported as failures), including one on dev itself. The suite | |
| # has grown - segment conformance and adapters, the edge runtime, per-engine | |
| # cache indices - and MinGW is the slowest toolchain we build on. | |
| # 2026-10-05: the 35 minutes set then were reached in turn. Over the last | |
| # two days the job took 21 to 34 minutes when it passed, and was cancelled | |
| # at 35 three times with every test passed so far (#1907, #1911 and a dev | |
| # run), after #1901 added its suites. 60 leaves room for the suites still | |
| # coming; a real hang still ends the job. | |
| # 2026-10-20: reached again. The job took 45 to 57 minutes when it passed | |
| # (#2265 twice, #2266), and was cancelled at 60 twice, once 5 seconds after | |
| # its 2695 tests had passed (#2267). 90. | |
| timeout-minutes: 90 | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 | |
| with: | |
| msystem: UCRT64 | |
| update: false | |
| install: >- | |
| make | |
| mingw-w64-ucrt-x86_64-gcc | |
| mingw-w64-ucrt-x86_64-python | |
| - name: make check | |
| run: make -C c check | |
| # The Windows engine as this branch builds it. Small, and it makes an | |
| # antivirus false-positive report (#527) verifiable on a PR instead of | |
| # only after a release is tagged and published. | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: colibri-windows-exe | |
| path: c/colibri.exe | |
| k3-tiny: | |
| name: Linux (generated tiny K3 vendor oracle) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| cache-dependency-path: c/tools/requirements-kimi-k3-tiny.txt | |
| - name: Install pinned tiny-fixture dependencies | |
| run: python -m pip install -r c/tools/requirements-kimi-k3-tiny.txt | |
| - name: Generate fixture and run vendor oracle | |
| run: make -C c kimi-k3-tiny-check | |
| mimo-tiny: | |
| name: Linux (generated tiny MiMo-V2.6 vendor oracle, text + vision) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| cache-dependency-path: c/tools/requirements-mimo-tiny.txt | |
| - name: Install pinned tiny-fixture dependencies | |
| run: python -m pip install -r c/tools/requirements-mimo-tiny.txt | |
| - name: Generate fixture and run vendor oracle | |
| run: make -C c mimo-tiny-check | |
| - name: "Brio and the prompt cache: photos exact against a cold engine" | |
| run: make -C c mimo-tiny-serve-check | |
| - name: Same oracle under ASan and UBSan | |
| run: | | |
| make -C c clean >/dev/null | |
| make -C c mimo EXTRA_CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" | |
| ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \ | |
| python c/tests/mimo_tiny_harness.py --binary c/mimo --fixture c/mimo_tiny | |
| cd c && ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \ | |
| python -m unittest tests.test_mimo_prefix_serve tests.test_brio_serve.MimoBrioServe \ | |
| tests.test_systemone_api.MimoSystemOneEndToEnd | |
| v4-tiny: | |
| name: Linux (generated tiny V4 target oracle) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| cache-dependency-path: c/tools/requirements-deepseek-v4-tiny.txt | |
| - name: Install pinned tiny-fixture dependencies | |
| run: python -m pip install -r c/tools/requirements-deepseek-v4-tiny.txt | |
| - name: Generate fixture and run target oracle | |
| run: make -C c deepseek-v4-tiny-check | |
| glm53-oracle: | |
| name: Linux (generated GLM-5.3 oracle contract) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| cache-dependency-path: c/tools/requirements-glm53-tiny.txt | |
| - name: Install pinned oracle dependencies | |
| run: python -m pip install -r c/tools/requirements-glm53-tiny.txt | |
| - name: Generate and validate oracle contract | |
| run: | | |
| python c/tools/make_glm53_tiny.py --output /tmp/glm53_tiny | |
| python c/tools/check_glm53_checkpoint.py --model /tmp/glm53_tiny | |
| # The dashboard's Brain and Profile tabs read EMAP/HITS/PROF from the | |
| # engine's stdout. GLM-5.3-Flash emitted none of them and both tabs were | |
| # blank (Discord, 2026-09-06). This drives the built engine through | |
| # `coli serve` and the /experts and /profile endpoints on a streaming | |
| # fixture, the same path the dashboard takes, so a family that stops | |
| # emitting a line fails here and not on a user's screen. | |
| - name: Build glm53 and check the dashboard contract | |
| run: | | |
| make -C c glm53 | |
| python c/tools/make_glm53_multimodal_tiny.py --output /tmp/glm53_mm | |
| python c/tools/make_glm53_streaming_pair.py --fixture /tmp/glm53_mm --output /tmp/glm53_stream | |
| python c/tools/make_edge_tiny_tokenizer.py --vocab-size 281 /tmp/glm53_stream-i4 | |
| cd c && COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_dashboard | |
| COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_context_exceeded | |
| COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_hostile_weights | |
| COLI_GLM53_FIXTURE=/tmp/glm53_mm python -m unittest -v tests.test_glm53_hostile_weights.Glm53MatrixShapeTest | |
| COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_cluster | |
| # The two stdlib-only oracles used to be argparse scripts that `make | |
| # test-python` collected as zero tests (#1700). They run here, on the | |
| # fixtures generated above, token-exact against transformers in f32. | |
| - name: Run the GLM-5.3 tiny oracles | |
| run: | | |
| cd c && GLM53_TINY=/tmp/glm53_tiny GLM53_MM_TINY=/tmp/glm53_mm python -m unittest -v tests.test_glm53_oracles | |
| GLM53_TINY=/tmp/glm53_tiny COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_prefill_batch | |
| COLI_GLM53_FIXTURE=/tmp/glm53_mm python -m unittest -v tests.test_glm53_accept | |
| qwen36-oracle: | |
| name: Linux (generated qwen36 oracles) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| cache-dependency-path: c/tools/requirements-glm53-tiny.txt | |
| - name: Install pinned oracle dependencies | |
| run: python -m pip install -r c/tools/requirements-glm53-tiny.txt | |
| # Flint's layer (a sink per head, sliding-window layers, layers without RoPE, dense | |
| # layers in front, sigmoid routing) token for token against tools/make_flint_tiny.py's | |
| # float32 reference, the GGUF read in place with F32 and with Q8_0 experts. | |
| - name: Build qwen36 and run the Flint tiny oracles | |
| run: | | |
| make -C c qwen36 | |
| cd c | |
| for experts in F32 Q8_0; do | |
| python tools/make_flint_tiny.py --out /tmp/flint_$experts --emit-ref /tmp/flint_$experts/ref.json \ | |
| --gguf /tmp/flint_$experts/model.gguf --gguf-experts $experts | |
| COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json | |
| # the KV cache in float16, as a serve keeps it (a .json reference keeps float32 unless Q36_KV says) | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_KV=f16 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json | |
| # the sliding layers' KV in a ring of window + 9 rows (the least spare), shorter than the run | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json 2> ring.log | |
| grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; } | |
| done | |
| # Muse Spark's layer (residual gates, the experts at their own width between two | |
| # projections, a norm on every expert's output, capped logits) against | |
| # tools/make_muse_tiny.py's reference, F32 experts and Q8_0 ones (the reference then | |
| # quantizes the experts' inputs as the CPU's integer kernels do). | |
| - name: Run the Muse Spark tiny oracles | |
| run: | | |
| cd c | |
| python tools/make_muse_tiny.py --out /tmp/muse_F32 --emit-ref /tmp/muse_F32/ref.json --experts F32 | |
| COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/muse_F32/model.gguf ./qwen36 16 8 /tmp/muse_F32/ref.json | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/muse_F32/model.gguf ./qwen36 16 8 /tmp/muse_F32/ref.json 2> ring.log | |
| grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; } | |
| # Q8_0: the experts' inputs round to int8 from sums whose order differs from one CPU | |
| # to the next (a code apart moved a 0.106 margin at the 16th token on AVX-VNNI), so | |
| # this one is shorter and wider apart | |
| python tools/make_muse_tiny.py --out /tmp/muse_Q8_0 --emit-ref /tmp/muse_Q8_0/ref.json --experts Q8_0 --max-new 8 --min-gap 0.2 | |
| COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/muse_Q8_0/model.gguf ./qwen36 16 8 /tmp/muse_Q8_0/ref.json | |
| # LFM3's layers (short gated convolutions beside attention with a gate per head, the | |
| # full layers' partial rotation, every branch normalised) against | |
| # tools/make_lfm3_tiny.py's float32 reference, F32 matrices and Q8_0 ones. | |
| - name: Run the LFM3 tiny oracles | |
| run: | | |
| cd c | |
| python tools/make_lfm3_tiny.py --out /tmp/lfm3_F32 --emit-ref /tmp/lfm3_F32/ref.json | |
| COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_KV=f16 COLI_LOOKUP=0 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json 2> ring.log | |
| grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; } | |
| python tools/make_lfm3_tiny.py --out /tmp/lfm3_Q8_0 --emit-ref /tmp/lfm3_Q8_0/ref.json --dense Q8_0 --max-new 8 --min-gap 0.2 | |
| COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/lfm3_Q8_0/model.gguf ./qwen36 16 8 /tmp/lfm3_Q8_0/ref.json | |
| # LFM3-VL's tower (lfm3_vision.h) from an mmproj: a picture's rows within 1e-4 of the | |
| # reference's, and the continuation they lead to | |
| python tools/make_lfm3_tiny.py --out /tmp/lfm3_V --emit-ref /tmp/lfm3_V/ref.json --vision | |
| COLI_CUDA=0 COLI_VULKAN=0 Q36_MMPROJ=/tmp/lfm3_V/mmproj.gguf Q36_VIS_DUMP=/tmp/lfm3_V/rows.bin SNAP=/tmp/lfm3_V/model.gguf ./qwen36 16 8 /tmp/lfm3_V/ref.json | |
| python -c "import numpy as n; a=n.fromfile('/tmp/lfm3_V/rows.bin','f4'); b=n.fromfile('/tmp/lfm3_V/image_rows.f32','f4'); e=abs(a-b).max()/n.sqrt((b*b).mean()); print('LFM3 vision rows, worst error of the rms', e); assert a.shape==b.shape and e<1e-4" | |
| macos: | |
| # clang; libomp for the threaded path (Makefile falls back to | |
| # single-threaded automatically if it's ever missing). | |
| name: macOS (colibri + V4 platform gate) | |
| runs-on: macos-latest | |
| # `make check` here now takes about 14.5 minutes; at 15 the job was cut | |
| # off inside the Python suite with no test failed, and showed as red. | |
| # 2026-10-24 it took 24 minutes, and on the next run the suite printed | |
| # OK at 25:00, the moment the job was cut off: 35 leaves the room again. | |
| timeout-minutes: 35 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: install libomp | |
| run: brew install libomp | |
| - name: make check (V4 execution gated off) | |
| run: make -C c check |