name: Build ROCmFPX Certified Profiles # On-demand builds of Ciru's model-specific certified releases. These are # frozen branches with pinned toolchains and Composable Kernel revisions — # not part of the nightly matrix. The accelerated profile routes are # qualified on gfx1151 only; other families build the same branch and fall # back to its standard guarded execution paths at runtime. on: workflow_dispatch: inputs: profile: description: 'Certified profile to build' required: true type: choice options: - kairic-edge - promptforge default: 'kairic-edge' gfx_target: description: 'AMD GPU families (comma-separated)' required: false default: 'gfx1151' rocm_version: description: 'TheRock ROCm version (default: 7.15 certification pin)' required: false default: '7.15.0a20260718' rocmfpx_ref: description: 'ROCmFPX ref override (default: profile branch)' required: false default: '' create_release: description: 'Create a GitHub release after successful build' required: false default: true type: boolean env: PROFILE: ${{ github.event.inputs.profile || 'kairic-edge' }} GFX_TARGETS: ${{ github.event.inputs.gfx_target || 'gfx1151' }} ROCM_VERSION: ${{ github.event.inputs.rocm_version || '7.15.0a20260718' }} # Certified Composable Kernel revision shared by both profile guides CK_COMMIT: fdf4bb7fcc984811cef48ce817d89aac064b984a jobs: prepare-matrix: runs-on: ubuntu-24.04 outputs: ubuntu_matrix: ${{ steps.set-matrix.outputs.ubuntu_matrix }} steps: - name: Set matrix id: set-matrix run: | targets="${{ env.GFX_TARGETS }}" matrix_targets=$(echo "$targets" \ | tr ',' '\n' \ | sed 's/^ *//;s/ *$//' \ | jq -R . \ | jq -s '{gfx_target: .}' \ | jq -c) echo "ubuntu_matrix=$matrix_targets" >> $GITHUB_OUTPUT echo "Generated matrix: $matrix_targets" resolve-ref: runs-on: ubuntu-24.04 outputs: rocmfpx_ref: ${{ steps.set-ref.outputs.rocmfpx_ref }} steps: - name: Resolve profile branch id: set-ref run: | override="${{ github.event.inputs.rocmfpx_ref }}" case "${{ env.PROFILE }}" in kairic-edge) branch="release/kairic-edge-qwen38-27b-v1.2" ;; promptforge) branch="qwen3.8-activefpx-promptforge-v2.3" ;; *) echo "Unknown profile" >&2; exit 1 ;; esac ref="${override:-$branch}" echo "rocmfpx_ref=$ref" >> $GITHUB_OUTPUT echo "Building profile '${{ env.PROFILE }}' from ref: $ref" build-ubuntu: runs-on: ubuntu-24.04 needs: [prepare-matrix, resolve-ref] strategy: matrix: ${{fromJson(needs.prepare-matrix.outputs.ubuntu_matrix)}} fail-fast: false outputs: rocm_version: ${{ steps.set-outputs.outputs.rocm_version }} rocmfpx_commit_hash: ${{ steps.set-outputs.outputs.rocmfpx_commit_hash }} steps: - name: Free disk space run: curl -fsSL https://raw.githubusercontent.com/kou/arrow/e49d8ae15583ceff03237571569099a6ad62be32/ci/scripts/util_free_space.sh | bash - name: Checkout repository uses: actions/checkout@v4 - name: Clean up workspace run: | if [ -d "ROCmFPX" ]; then rm -rf ROCmFPX; fi if [ -d "/opt/rocm" ]; then sudo rm -rf /opt/rocm; fi if [ -f "rocm.tar.gz" ]; then rm -f rocm.tar.gz; fi - name: Install build dependencies run: | sudo apt update sudo apt install -y cmake ninja-build unzip curl patchelf - name: Download and extract pinned ROCm to /opt/rocm run: | rocm_version="${{ env.ROCM_VERSION }}" current_target="${{ matrix.gfx_target }}" s3_target="$current_target" if [[ "$current_target" = "gfx103X" || "$current_target" = "gfx110X" || "$current_target" = "gfx120X" ]]; then s3_target="${current_target}-all" fi rocm_url="https://rocm.nightlies.amd.com/tarball-multi-arch/therock-dist-linux-${s3_target}-${rocm_version}.tar.gz" echo "Downloading pinned ROCm: $rocm_url" echo "DETECTED_ROCM_VERSION=$rocm_version" >> $GITHUB_ENV sudo mkdir -p /opt/rocm curl -sL "$rocm_url" | sudo tar --use-compress-program=gzip -xf - -C /opt/rocm --strip-components=1 - name: Set ROCm environment variables run: | echo "HIP_PATH=/opt/rocm" >> $GITHUB_ENV echo "ROCM_PATH=/opt/rocm" >> $GITHUB_ENV echo "HIP_PLATFORM=amd" >> $GITHUB_ENV echo "HIP_CLANG_PATH=/opt/rocm/llvm/bin" >> $GITHUB_ENV echo "HIP_INCLUDE_PATH=/opt/rocm/include" >> $GITHUB_ENV echo "HIP_LIB_PATH=/opt/rocm/lib" >> $GITHUB_ENV echo "HIP_DEVICE_LIB_PATH=/opt/rocm/lib/llvm/amdgcn/bitcode" >> $GITHUB_ENV echo "/opt/rocm/bin:/opt/rocm/llvm/bin:$PATH" >> $GITHUB_PATH echo "LD_LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/lib64:/opt/rocm/llvm/lib:${LD_LIBRARY_PATH:-}" >> $GITHUB_ENV echo "LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/lib64:${LIBRARY_PATH:-}" >> $GITHUB_ENV echo "CPATH=/opt/rocm/include:${CPATH:-}" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=/opt/rocm/lib/pkgconfig:${PKG_CONFIG_PATH:-}" >> $GITHUB_ENV - name: Clone ROCmFPX profile branch run: | ref="${{ needs.resolve-ref.outputs.rocmfpx_ref }}" git clone --depth 1 --single-branch --branch "$ref" https://github.com/ciru-ai/ROCmFPX.git cd ROCmFPX commit_hash=$(git rev-parse HEAD) echo "ROCMFPX_COMMIT_HASH=$commit_hash" >> $GITHUB_ENV git log --oneline -1 - name: Prepare Composable Kernel working-directory: ${{ github.workspace }}/ROCmFPX run: | git clone https://github.com/ROCm/composable_kernel.git "${{ github.workspace }}/composable_kernel" git -C "${{ github.workspace }}/composable_kernel" checkout "${{ env.CK_COMMIT }}" if [ "${{ env.PROFILE }}" = "kairic-edge" ]; then patch="${{ github.workspace }}/ROCmFPX/patches/composable-kernel-gfx1151-iu4.patch" expected="ea57bca3793d35d884fc156b6c0bc33d8e672e3bb49f66ff67338503029d6af5" actual=$(sha256sum "$patch" | cut -d' ' -f1) if [ "$actual" != "$expected" ]; then echo "CK patch hash mismatch: $actual" >&2 exit 1 fi git -C "${{ github.workspace }}/composable_kernel" apply "$patch" git -C "${{ github.workspace }}/composable_kernel" diff --check echo "Composable Kernel patched for Kairic Edge IU4" fi - name: Build profile runtime working-directory: ROCmFPX run: | current_target="${{ matrix.gfx_target }}" if [ "$current_target" = "gfx110X" ]; then mapped_target="gfx1100;gfx1101;gfx1102;gfx1103" elif [ "$current_target" = "gfx103X" ]; then mapped_target="gfx1030;gfx1031;gfx1032;gfx1034" elif [ "$current_target" = "gfx120X" ]; then mapped_target="gfx1200;gfx1201" else mapped_target="$current_target" fi if [ -x /opt/rocm/bin/amdclang++ ]; then hip_compiler="/opt/rocm/bin/amdclang++" else hip_compiler="/opt/rocm/llvm/bin/clang++" fi hip_flags="" if [ "${{ env.PROFILE }}" = "kairic-edge" ]; then hip_flags="-DGGML_ROCMFPX_RDNA35_MMID_MAX_BATCH=5 -DGGML_ROCMFPX_MOE_MMVQ_ROWS_PER_BLOCK=4" fi # Flags mirror Ciru's certified build recipes # (docs/kairic-edge-gfx1151.md, docs/activefpx-promptforge-qwen38.md); # GPU targets are parameterized so non-certified families still get a # runnable build of the same branch. cmake -S . -B build-profile -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER="$(command -v gcc)" \ -DCMAKE_CXX_COMPILER="$(command -v g++)" \ -DCMAKE_HIP_COMPILER="$hip_compiler" \ -DCMAKE_HIP_FLAGS="$hip_flags" \ -DCMAKE_PREFIX_PATH="/opt/rocm" \ -DBUILD_SHARED_LIBS=ON \ -DGGML_CPU=ON -DGGML_OPENMP=ON -DGGML_HIP=ON -DGGML_CUDA=OFF \ -DGGML_VULKAN=OFF -DGGML_HIP_FORCE_MMQ=ON -DGGML_HIP_GRAPHS=ON \ -DGGML_HIP_MMQ_MFMA=ON -DGGML_HIP_NO_VMM=ON \ -DGGML_HIP_ROCWMMA_FATTN=OFF -DGGML_NATIVE=ON \ -DAMDGPU_TARGETS="$mapped_target" -DGPU_BUILD_TARGETS="$mapped_target" \ -DPROMPTFORGE_CK_ROOT="${{ github.workspace }}/composable_kernel" \ -DLLAMA_BUILD_WEBUI=OFF \ -DGGML_BUILD_TESTS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_SERVER=ON cmake --build build-profile --target llama-server -j "$(nproc)" - name: Copy ROCm core libs to build directory run: | build_bin_path="ROCmFPX/build-profile/bin" rocblas_lib_path="/opt/rocm/lib/rocblas/library" if [ -d "$rocblas_lib_path" ]; then dest_rocblas_path="$build_bin_path/rocblas/library" mkdir -p "$(dirname "$dest_rocblas_path")" cp -r "$rocblas_lib_path" "$(dirname "$dest_rocblas_path")/" fi hipblaslt_lib_path="/opt/rocm/lib/hipblaslt/library" if [ -d "$hipblaslt_lib_path" ]; then dest_hipblaslt_path="$build_bin_path/hipblaslt/library" mkdir -p "$(dirname "$dest_hipblaslt_path")" cp -r "$hipblaslt_lib_path" "$(dirname "$dest_hipblaslt_path")/" fi cp -v /opt/rocm/lib/libhipblas.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/librocblas.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libamdhip64.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/librocsolver.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libroctx64.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libhipblaslt.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_liblzma.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/librocprofiler-register.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libamd_comgr.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libamd_comgr_loader.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/libhsa-runtime64.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_numa.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/librocroller.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/liborigami.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/librocm_kpack.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_z.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_zstd.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/llvm/lib/libLLVM.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/llvm/lib/libclang-cpp.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_elf.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_drm.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_drm_amdgpu.so* "$build_bin_path/" 2>/dev/null || true cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_bz2.so* "$build_bin_path/" 2>/dev/null || true - name: Set RPATH for portable distribution run: | cd ROCmFPX/build-profile/bin for file in *.so* llama-*; do [ -f "$file" ] && [ ! -L "$file" ] && patchelf --set-rpath '$ORIGIN' "$file" 2>/dev/null || true done - name: Verify Linux Binaries and Dependencies run: | cd ROCmFPX/build-profile/bin export LD_LIBRARY_PATH="$(pwd):${LD_LIBRARY_PATH:-}" ./llama-server --version ldd ./llama-server > /tmp/profile-ldd.txt 2>&1 || true if grep -Eiq 'not found|version .* required by .* not found' /tmp/profile-ldd.txt; then cat /tmp/profile-ldd.txt exit 1 fi echo "Profile binary verification successful!" - name: Upload build artifacts id: upload-artifacts uses: actions/upload-artifact@v4 with: name: rocmfpx-ubuntu-rocm-${{ matrix.gfx_target }}-${{ env.PROFILE }}-x64 path: ROCmFPX/build-profile/bin/ retention-days: 30 - name: Set job outputs id: set-outputs run: | rocm_version="${DETECTED_ROCM_VERSION:-${{ env.ROCM_VERSION }}}" echo "rocm_version=$rocm_version" >> $GITHUB_OUTPUT echo "rocmfpx_commit_hash=${ROCMFPX_COMMIT_HASH}" >> $GITHUB_OUTPUT - name: Clean up workspace if: always() run: | if [ -f "rocm.tar.gz" ]; then rm -f rocm.tar.gz; fi if [ -d "/opt/rocm" ]; then sudo rm -rf /opt/rocm; fi if [ -d "ROCmFPX" ]; then rm -rf ROCmFPX; fi if [ -d "composable_kernel" ]; then rm -rf composable_kernel; fi create-release: needs: [prepare-matrix, resolve-ref, build-ubuntu] runs-on: ubuntu-24.04 permissions: contents: write if: | always() && needs.build-ubuntu.result == 'success' && github.event_name == 'workflow_dispatch' && (github.event.inputs.create_release == 'true' || github.event.inputs.create_release == null) steps: - name: Checkout repository uses: actions/checkout@v4 - name: Download all build artifacts uses: actions/download-artifact@v4 with: path: ./all-artifacts - name: Generate release tag id: generate-tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | existing_tags=$(gh release list --limit 1000 --json tagName --jq '.[].tagName' | grep -E '^b[0-9]{4}$' | sort -V || echo "") if [ -z "$existing_tags" ]; then next_number=1000 else highest_tag=$(echo "$existing_tags" | tail -n 1) highest_number=$(echo "$highest_tag" | sed 's/^b//') next_number=$((highest_number + 1)) fi TAG=$(printf "b%04d" $next_number) echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "Generated release tag: ${TAG}" - name: Create archives for all target artifacts run: | targets="${{ env.GFX_TARGETS }}" PROFILE="${{ env.PROFILE }}" TAG="${{ steps.generate-tag.outputs.tag }}" IFS=',' read -ra TARGET_ARRAY <<< "$targets" for target in "${TARGET_ARRAY[@]}"; do target=$(echo "$target" | xargs) artifact_name="rocmfpx-ubuntu-rocm-${target}-${PROFILE}-x64" artifact_dir="./all-artifacts/${artifact_name}" final_archive_name="rocmfpx-${TAG}-ubuntu-rocm-${target}-${PROFILE}-x64" ciru_archive_name="ciru-rocmfpx-${TAG}-ubuntu-rocm-${target}-${PROFILE}-x64" ciru_main_archive="ciru-rocmfpx-${TAG}-ubuntu-rocm-${target}-x64" if [ -d "$artifact_dir" ]; then cd "$artifact_dir" zip -r "../../${final_archive_name}.zip" * zip -r "../../${ciru_archive_name}.zip" * zip -r "../../${ciru_main_archive}.zip" * cd ../../ else echo "Warning: Artifact directory not found: $artifact_dir" fi done - name: Create Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="${{ steps.generate-tag.outputs.tag }}" PROFILE="${{ env.PROFILE }}" ROCM_VERSION="${{ env.ROCM_VERSION }}" ROCMFPX_REF="${{ env.ROCMFPX_REF_RESOLVED }}" ROCMFPX_COMMIT_HASH="${{ needs.build-profile.outputs.rocmfpx_commit_hash }}" CK_COMMIT="${{ env.CK_COMMIT }}" targets="${{ env.GFX_TARGETS }}" upload_files="" for f in rocmfpx-${TAG}-*.zip ciru-rocmfpx-${TAG}-*.zip; do if [ -f "$f" ]; then upload_files="$upload_files $f" fi done case "$PROFILE" in kairic-edge) model_docs="## Model artifacts (download from [jcbtc/Qwen3.8-27B-IU4-Kairic-Edge](https://huggingface.co/jcbtc/Qwen3.8-27B-IU4-Kairic-Edge)) - \`Qwen3.8-27B-IU4-Kairic-Edge.gguf\` - \`Qwen3.8-27B-Kairic-IU4-FFN.pfs\` - \`Qwen3.8-27B-Kairic-IU4-GDN.pfs\` - \`Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs\` ## Required environment \`\`\`bash export KAIRIC_FFN_SIDECAR=/path/to/Qwen3.8-27B-Kairic-IU4-FFN.pfs export KAIRIC_GDN_SIDECAR=/path/to/Qwen3.8-27B-Kairic-IU4-GDN.pfs export KAIRIC_GDN_OUTPUT_SIDECAR=/path/to/Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs \`\`\` Startup fails closed if sidecars are missing. The log line \`Kairic Edge: enabled (ngram=24/64/64, M65 verifier=strict-compact)\` confirms the qualified route. Context checkpoints are required (\`--ctx-checkpoints 32 --cache-ram 8192 --cache-prompt\`). Sampling/tool calls require \`KAIRIC_EDGE_COMPATIBILITY_MODE=1\`. See [docs/kairic-edge-gfx1151.md](https://github.com/ciru-ai/ROCmFPX/blob/${REF}/docs/kairic-edge-gfx1151.md)." ;; promptforge) model_docs="## Model artifacts (download from [jcbtc/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge](https://huggingface.co/jcbtc/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge)) - \`Qwen3.8-27B-CIRU-ActiveFPX-PromptForge.gguf\` - \`Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-FFN.pfs\` - \`Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-GDN.pfs\` ## Required environment \`\`\`bash export PROMPTFORGE_SIDECAR=/path/to/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-FFN.pfs export PROMPTFORGE_GDN_SIDECAR=/path/to/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-GDN.pfs export PROMPTFORGE_MODE=m2048_fused_tail1476 export GGML_CUDA_GRAPH_OPT=0 export LLAMA_MTP_CPU_ARGMAX_FASTPATH=1 \`\`\` Startup fails closed if sidecars or qualified kernel routes are unavailable. Launch with \`--ctx-checkpoints 32 --cache-ram 8192 --cache-prompt\`. See [docs/activefpx-promptforge-qwen38.md](https://github.com/ciru-ai/ROCmFPX/blob/${REF}/docs/activefpx-promptforge-qwen38.md)." ;; esac gh release create "$TAG" \ --title "Ciru ROCmFPX ${PROFILE} ${TAG}" \ --notes "**Profile**: ${PROFILE} **Operating System(s)**: ubuntu **GPU Target(s)**: $targets **ROCm Version**: $ROCM_VERSION (certification pin) **ROCmFPX Repository**: ciru-ai/ROCmFPX **ROCmFPX Ref**: $REF @ $ROXMFPX_HASH **Build Date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC') > [!WARNING] > The accelerated profile routes are qualified on **Strix Halo (\`gfx1151\`)** > only. On other GPU families this binary runs the same branch through its > documented guarded fallback paths; the specialized routes will not engage. $model_docs" \ $upload_files - name: Trigger Homebrew Tap Formula Update if: success() env: TAP_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} TAG: ${{ steps.generate-tag.outputs.tag }} run: | if [ -n "$TAP_TOKEN" ]; then echo "Notifying Homebrew Tap of new release: $TAG" GITHUB_TOKEN="$TAP_TOKEN" gh api -X POST "/repos/Heretek-AI/homebrew-tap/dispatches" \ -f event_type="builder-release" \ -f client_payload[builder]="ciru-rocmfpx" \ -f client_payload[release_tag]="$TAG" echo "[✓] Successfully dispatched event to Heretek-AI/homebrew-tap" else echo "::notice title=Tap Dispatch Skipped::TAP_GITHUB_TOKEN secret is not configured in repository secrets. Cross-repository dispatch was skipped. homebrew-tap's scheduled autobump.yml workflow (runs every 4 hours) will automatically synchronize this release ($TAG). To enable instant dispatch, create a GitHub Personal Access Token (PAT) with 'repo' scope and add it as secret 'TAP_GITHUB_TOKEN'." fi