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
70 changes: 70 additions & 0 deletions .github/workflows/bitsandbytes-examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# bitsandbytes examples guard - project thin trigger.
#
# Calls the common engine .github/workflows/examples-template.yml;
# this file only declares what varies per project: schedule,
# concurrency, upstream repo, dispatch input forwarding, and matrix
# parallelism. The guard loop (release + workflow-files monitor /
# manifest-check / run-example matrix / validate-results /
# save-monitor-state) lives in the engine. See examples-guards/README.md
# for the design.

name: bitsandbytes-examples

concurrency:
# Three groups: schedule runs serialize (a run cancelled mid-way
# loses its outcome writeback, and the outcome drives the next
# cycle's retry); each PR serializes per number so a new push
# cancels the stale run instead of queueing another matrix on the
# NPU runners; each manual run stands alone. Group names are
# repository-wide, so the PR group carries the workflow name: a bare
# pr-<number> would let two projects' runs on one PR cancel each
# other.
group: ${{ github.event_name == 'schedule' && 'bitsandbytes-examples-schedule' || (github.event_name == 'pull_request' && format('bitsandbytes-examples-pr-{0}', github.event.pull_request.number)) || format('manual-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

on:
# Daily at 04:00 Beijing time (UTC 20:00). 18:00 UTC is
# accelerate-examples; 19:00 UTC is the tgi examples slot.
schedule:
- cron: '0 20 * * *'
workflow_dispatch:
inputs:
target_ref:
description: Branch, tag, or SHA to test (empty = latest upstream release; falls back to main on API failure)
required: false
default: ''
type: string
# PR runs need no merge: GitHub executes the workflow files from the
# PR merge ref, so a newly added trigger and engine run on the PR
# that adds them. Whether a fork PR waits for a maintainer's approval
# first is the repository's fork-PR policy. `pull_request`, not
# `pull_request_target`: the declared read permissions are all the
# engine needs. paths lists only this project's files, as the
# quick-start triggers do: a PR that touches only the shared engine,
# scripts or schema starts no project's matrix.
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
paths:
- 'examples-guards/bitsandbytes/**'
- '.github/workflows/bitsandbytes-examples.yml'

# Engine jobs query the GitHub Job API (validate-results); called
# workflows run with the caller's GITHUB_TOKEN permissions.
permissions:
actions: read
contents: read

jobs:
bitsandbytes-examples:
uses: ./.github/workflows/examples-template.yml
with:
# Namespaces cache keys (examples-monitor-state-bitsandbytes_*),
# artifact names and the scheduling paths under
# examples-guards/bitsandbytes.
project: bitsandbytes
upstream_repo: bitsandbytes-foundation/bitsandbytes
target_ref: ${{ inputs.target_ref }}
# Two supported entries, different hub repos. Parallel is safe;
# they do not write the same snapshot.
max_parallel: 2
43 changes: 43 additions & 0 deletions examples-guards/bitsandbytes/constraints-npu.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Keep CUDA metapackages out of bitsandbytes example installs.
# The runners are aarch64 without CUDA. A resolver on the default index
# will pull nvidia-* / cuda-* as transitive deps; those wheels fail to
# install or sit unused. torch itself is installed from the CPU index
# as a +cpu wheel, not from this file.
cuda-toolkit<0
cuda-python<0
cuda-bindings<0
cuda-core<0
cuda-pathfinder<0
flashinfer-python<0
nvidia-cublas<0
nvidia-cuda-runtime<0
nvidia-cuda-nvrtc<0
nvidia-cuda-cupti<0
nvidia-cudnn<0
nvidia-cudnn-frontend<0
nvidia-cufft<0
nvidia-curand<0
nvidia-cusolver<0
nvidia-cusparse<0
nvidia-cutlass-dsl<0
nvidia-cutlass-dsl-libs-base<0
nvidia-cutlass-dsl-libs-core<0
nvidia-cutlass-dsl-libs-cu12<0
nvidia-ml-py<0
nvidia-nccl<0
nvidia-nvjitlink<0
nvidia-nvtx<0
nvidia-cublas-cu12<0
nvidia-cuda-nvdisasm<0
nvidia-cuda-runtime-cu12<0
nvidia-cuda-nvrtc-cu12<0
nvidia-cuda-cupti-cu12<0
nvidia-cudnn-cu12<0
nvidia-cufft-cu12<0
nvidia-curand-cu12<0
nvidia-cusolver-cu12<0
nvidia-cusparse-cu12<0
nvidia-cusparselt-cu12<0
nvidia-nccl-cu12<0
nvidia-nvjitlink-cu12<0
nvidia-nvtx-cu12<0
47 changes: 47 additions & 0 deletions examples-guards/bitsandbytes/examples_manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
version: 1
scan:
root: examples
include_extensions: ['.py']
max_depth: 3
supported:
# CPU AdamW finetune. The script keeps tensors on CPU; a green run
# means bitsandbytes' CPU optimizer still trains, not that NPU
# inference worked. Success is the script's own loss line.
- path: examples/cpu/cpu_training.py
profile: cpu
runner: linux-aarch64-a2-1
npu_devices: '0'
image: swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.1.0-910b-ubuntu22.04-py3.12
overlay_args:
- --steps 5
- --log_interval 1
- --max_length 64
- --batch_size 1
timeout_minutes: 90
# 8-bit gemma-2-2b-it, torch.compile, 32 new tokens. The run script
# requires NPUCachingAllocator in the log after exit 0.
- path: examples/compile_inference.py
profile: npu
runner: linux-aarch64-a2-1
npu_devices: '0'
image: swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.1.0-910b-ubuntu22.04-py3.12
timeout_minutes: 120
unsupported:
# tag 0.50.2 examples/int8_inference_huggingface.py:
# model_name = "meta-llama/Llama-2-7b-hf"; Hub API reports gated=manual.
# max_memory is built from torch.cuda.mem_get_info()[0], and
# n_gpus = torch.cuda.device_count().
# LlamaForCausalLM.from_pretrained(..., device_map="auto", load_in_8bit=True).
- examples/int8_inference_huggingface.py
# tag 0.50.2 examples/xpu/paged_xpu_training.py:
# --device defaults to "xpu". main() then asserts
# hasattr(torch, "xpu") and torch.xpu.is_available(), or
# torch.cuda.is_available() when --device cuda. There is no npu branch.
# from_pretrained(..., device_map=device) and the batch is .to(device).
- examples/xpu/paged_xpu_training.py
# tag 0.50.2 examples/xpu/benchmark_paged_memory.py:
# --device defaults to "xpu". get_accelerator() returns torch.xpu or,
# for any other name, torch.cuda. The model is
# LlamaForCausalLM(config).to(dtype=dtype, device=args.device), and
# peak memory is read from that accelerator. There is no npu branch.
- examples/xpu/benchmark_paged_memory.py
25 changes: 25 additions & 0 deletions examples-guards/bitsandbytes/scripts/assert_native_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Fail if bitsandbytes fell back to its mock native library."""
from __future__ import annotations

import sys

import bitsandbytes
import bitsandbytes.cextension as ce

MOCK = 'ErrorHandlerMockBNBNativeLibrary'


def main() -> int:
name = type(ce.lib).__name__
print('bitsandbytes', bitsandbytes.__version__)
print('BNB_BACKEND', ce.BNB_BACKEND)
print('lib', name)
if name == MOCK:
print('native library missing; CPU .so did not load', file=sys.stderr)
return 1
return 0


if __name__ == '__main__':
raise SystemExit(main())
36 changes: 36 additions & 0 deletions examples-guards/bitsandbytes/scripts/assert_npu_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Fail unless the NPU compile stack matches the pinned pair."""
from __future__ import annotations

import sys
from importlib.metadata import version

import torch
import torch_npu
import triton


def main() -> int:
torch_ok = torch.__version__.startswith('2.10.0')
npu_ok = torch_npu.__version__.startswith('2.10.0')
triton_ok = version('triton-ascend') == '3.2.2'
npu_up = bool(torch.npu.is_available())
print('torch', torch.__version__)
print('torch_npu', torch_npu.__version__)
print('npu_count', torch.npu.device_count())
print('triton-ascend', version('triton-ascend'))
print('triton_file', triton.__file__)
print('npu_available', npu_up)
if not torch_ok or not npu_ok or not triton_ok or not npu_up:
print(
'NPU compile stack mismatch: '
f'torch={torch.__version__} torch_npu={torch_npu.__version__} '
f'triton-ascend={version("triton-ascend")} npu_available={npu_up}',
file=sys.stderr,
)
return 1
return 0


if __name__ == '__main__':
raise SystemExit(main())
27 changes: 27 additions & 0 deletions examples-guards/bitsandbytes/scripts/expand_overlay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Print OVERLAY_ARGS as a shell-quoted token list for run_example.sh."""
from __future__ import annotations

import json
import os
import shlex
import sys


def main() -> int:
raw = os.environ.get('OVERLAY_ARGS', '').strip()
if not raw or raw in ('null', '""'):
return 0
items = json.loads(raw)
if not isinstance(items, list) or not all(isinstance(item, str) for item in items):
print('OVERLAY_ARGS must be a JSON array of strings', file=sys.stderr)
return 1
tokens: list[str] = []
for item in items:
tokens.extend(shlex.split(os.path.expandvars(item), posix=True))
print(' '.join(shlex.quote(token) for token in tokens))
return 0


if __name__ == '__main__':
raise SystemExit(main())
Loading
Loading