XAIGPUARC ANGEL VERSION FULL WORKING PLUS EARLY FLASH ATTENTION 1/3

Words
2320
Reading
11 min
Listen
Play
10M

#!/bin/bash
#----------------------------------------------------------------------------------
#-XAIGPUARC
#-Automatischer Build + Run von llama.cpp mit Intel oneAPI / SYCL Backend
#-Getestet und Optimiert mit fünf unterschiedlichen ARC Endgeräten auf Garuda Linux
#-Intel ARC A770 (16GiB)/ 750 (8GiB)/
#-Single + Dual GPU auf AMD Ryzen 2600/ 2700x/ Intel 6700K @Z170
#-Intel 12700h/12650h + A730m 12 GiB + 6GiB /
#-Intel Core 155H + ARC iGPU (16GiB RAM/ 11,5 GiB-VRAM)
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
#-Globale Variablen-
set -euo pipefail
IFS=$'\n\t'

Standardwerte

PRECISION="FP16"
DEVICE="ARC" # Standard-Fallback
LLAMA_CPP_DIR="llama.cpp"
BUILD_DIR="${BUILD_DIR:-XAIGPUARC}"

GGML_SYCL_CPP="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/ggml-sycl.cpp"

CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}"
NPROC="${NPROC:-$(nproc)}"

LLAMA_CLI_PATH="bin/llama-cli"
LS_SYCL_DEVICE_PATH="bin/llama-ls-sycl-device"

---------------------------------

oneAPI + SYCL Umgebungsvariablen

export TCM_ROOT="${TCM_ROOT:-/opt/intel/oneapi/tcm/latest}"
export SYCL_CACHE_PERSISTENT=1
export OCL_ICD_FILENAMES=""
export ZES_ENABLE_SYSMAN=1
export CCACHE_DIR="$HOME/.ccache"
export COMPILER_VERSION="2025.0"

--00-- Hilfsfunktionen ----------------------------------------------------------

log() { echo -e "🔷 $"; }
success() { echo -e "✅ $
"; }
error() { echo -e "❌ $\n"; }
warning() { echo -e "⚠️ $
\n"; }
err() { error "$"; }
warn() { echo -e "⚠️ $
"; }

#-- [0] Umgebung vorbereiten - FINALER FIX: Extrem robuste Fallback-Logik

prepare_environment() {
log "Aktiviere Intel oneAPI Umgebung (MKL, SYCL/C++ Headers)..."
local SETVARS_PATH="/opt/intel/oneapi/setvars.sh"

if [ ! -f "$SETVARS_PATH" ]; then
    err "Die Intel oneAPI Umgebung wurde nicht gefunden unter: $SETVARS_PATH. Bitte zuerst Intel oneAPI installieren!"
    exit 1
fi

log "Sourcing setvars.sh, um DPCPP_ROOT und MKL_ROOT zu setzen..."
# Source ohne Pipe, Fehler umleiten
source "$SETVARS_PATH" --force 2>/dev/null

# --- KRITISCHER FIX FÜR LEERE VARIABLEN ---
local ONEAPI_ROOT_FALLBACK="/opt/intel/oneapi"
local COMPILER_VERSION_FALLBACK="${COMPILER_VERSION:-2025.0}"

DPCPP_ROOT="${DPCPP_ROOT:-${ONEAPI_ROOT_FALLBACK}/compiler/${COMPILER_VERSION_FALLBACK}}"
MKL_ROOT="${MKL_ROOT:-${ONEAPI_ROOT_FALLBACK}/mkl/${COMPILER_VERSION_FALLBACK}}"
ONEAPI_ROOT="${ONEAPI_ROOT:-${ONEAPI_ROOT_FALLBACK}}"

export DPCPP_ROOT
export MKL_ROOT
export ONEAPI_ROOT
export CPATH="${CPATH:-}:${MKL_ROOT}/include"

local LIB_DIR="/opt/intel/oneapi/compiler/latest/lib:/opt/intel/oneapi/mkl/latest/lib"
export LD_LIBRARY_PATH="./${BUILD_DIR}/bin:${LIB_DIR}:${LD_LIBRARY_PATH:-}"

# -Prüfen ob Compiler existiert-
if ! command -v icx &>/dev/null; then
    err "Intel compiler (icx/icpx) not found. Check your oneAPI installation."
    exit 1
fi

log "✅ oneAPI environment loaded (DPCPP_ROOT=${DPCPP_ROOT} und MKL_ROOT=${MKL_ROOT})."

}

#-- [1] Projekt-Setup -------------------------------------------------------------

setup_project() {
log "📦 Setting up llama.cpp project..."
if [ ! -d "${LLAMA_CPP_DIR}" ]; then
log " -> Klonen von llama.cpp..."
git clone https://github.com/ggerganov/llama.cpp "${LLAMA_CPP_DIR}"
if [ $? -ne 0 ]; then
err "❌ Klonen von llama.cpp fehlgeschlagen. Breche ab."
exit 1
fi
fi

if pushd "${LLAMA_CPP_DIR}" > /dev/null; then
    log "   -> Aktualisiere und initialisiere Submodule..."
    git pull
    git submodule update --init --recursive
    popd > /dev/null
    success "✅ llama.cpp ready. (Repo und Submodule sind vorhanden)."
else
    err "❌ Fehler: Das Hauptverzeichnis '${LLAMA_CPP_DIR}' wurde nicht gefunden. Breche ab."
    exit 1
fi

}

#-- [05] Robuster Single-Shot Patch für Header-Probleme und Kernel-Integration -------------------------
patch_llama_cpp() {
log "🔷 🔷 🩹 Patches für ggml-sycl anwenden (Header & CMake & Kernel-Dispatch-Registrierung)..."
local DPCT_HELPER_FILE="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/dpct/helper.hpp"
local CMAKE_LISTS_FILE="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/CMakeLists.txt"
local CUSTOM_KERNEL_DIR="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/custom_kernels"
local CUSTOM_KERNEL_SRC="${CUSTOM_KERNEL_DIR}/ggml_flash_attention_sycl.cpp"
local CUSTOM_KERNEL_CMAKE="${CUSTOM_KERNEL_DIR}/CMakeLists.txt"

# NEU: Pfad zur Haupt-Dispatch-Datei für Patch 4
# Diese lokale Definition ist nötig, falls die globale Definition nicht übergeben wird.
# Sie ist hier erlaubt, weil sie innerhalb einer Funktion ist.
local GGML_SYCL_CPP="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/ggml-sycl.cpp"

# NEU: Lokaler Pfad zum XARCFA Kernel im Home-Verzeichnis
local KERNEL_SOURCE_LOCAL="ggml_flash_attention_sycl.cpp"

# --- Patch 1: dpct/helper.hpp (MKL/Math Header Korrektur) ---
if [ -f "$DPCT_HELPER_FILE" ]; then
    log "🔷      -> Patch 1/5: dpct/helper.hpp anpassen (Header Fix zu sycl/ext/intel/math.hpp)."
    # Versuche den einfachen sed-Befehl (ersetzt oneapi durch intel math.hpp)
    if sed -i 's|#include <sycl/ext/oneapi/math.hpp>|#include <sycl/ext/intel/math.hpp>|g' "$DPCT_HELPER_FILE"; then
         log "🔷      -> ✅ Patch 1/5 erfolgreich (Standard)."
    # Fallback auf den komplexeren sed-Befehl, falls der User ihn hat
    elif sed -i 's|#if \!defined(DPCT\_USM\_LEVEL\_NONE) && defined(DPCT\_ENABLE\_MKL\_MATH).*#endif|#include <sycl/ext/intel/math.hpp>|g' "$DPCT_HELPER_FILE"; then
        log "🔷      -> ✅ Patch 1/5 erfolgreich (Fallback)."
    else
        error "❌ Patch 1 (dpct/helper.hpp) ist fehlgeschlagen."
        return 1
    fi
else
    error "❌ Patch 1 fehlgeschlagen: **dpct/helper.hpp** nicht gefunden."
    return 1
fi

# --- Patch 2: Flash Attention Kernel in Build-System integrieren ---
log "🔷      -> Patch 2/5: XARCFA Kernel in das Build-System integrieren."

# 2a: Erstelle Kernel-Ordner und kopiere/erstelle Kernel-Datei
if [ ! -d "$CUSTOM_KERNEL_DIR" ]; then
    mkdir -p "$CUSTOM_KERNEL_DIR"
    log "🔷         -> Ordner '${CUSTOM_KERNEL_DIR}' erstellt."
fi

# Kopiere den XARCFA Kernel, falls er im lokalen Verzeichnis existiert.
if [ -f "$KERNEL_SOURCE_LOCAL" ]; then
    cp "$KERNEL_SOURCE_LOCAL" "$CUSTOM_KERNEL_SRC"
    log "🔷         -> ✅ XARCFA Kernel von './${KERNEL_SOURCE_LOCAL}' nach '${CUSTOM_KERNEL_SRC}' kopiert."
fi

# Sicherstellen, dass die Zieldatei existiert, auch wenn der Kernel noch nicht da war
if [ ! -f "$CUSTOM_KERNEL_SRC" ]; then
    echo "// Platzhalter für ggml_flash_attention_sycl.cpp (Kernel-Datei fehlte im Home-Verzeichnis)" > "$CUSTOM_KERNEL_SRC"
    warning "⚠️ Kernel-Datei '${KERNEL_SOURCE_LOCAL}' nicht im Home-Verzeichnis gefunden. Es wurde ein Platzhalter erstellt."
fi

# Erstelle die CMakeLists.txt für unseren Kernel

echo "

CMakeLists.txt für Flash Attention Kernel (OBJECT-Library)

OBJECT-Library wird verwendet, um die Objektdateien direkt in die Hauptbibliothek einzufügen.

add_library(ggml_flash_attention OBJECT
ggml_flash_attention_sycl.cpp
)

Stelle sicher, dass die Compiler-Optionen für SYCL übernommen werden

target_include_directories(ggml_flash_attention PRIVATE ${GGML_SYCL_INCLUDE_DIRS})
target_compile_options(ggml_flash_attention PUBLIC ${GGML_SYCL_COMPILE_FLAGS})
" > "$CUSTOM_KERNEL_CMAKE"
log "🔷 -> CMakeLists.txt für Kernel als OBJECT-Library erstellt."

# 2b: Füge das Kernel-Unterverzeichnis zur Haupt-ggml-sycl CMakeLists.txt hinzu
local ADD_SUBDIR_LINE="add_subdirectory(custom_kernels)"
if ! grep -q "${ADD_SUBDIR_LINE}" "$CMAKE_LISTS_FILE"; then
    if sed -i "/add_subdirectory(dpct)/a ${ADD_SUBDIR_LINE}" "$CMAKE_LISTS_FILE"; then
        log "🔷         -> ✅ Patch 2/5 erfolgreich: custom_kernels zu Haupt-CMake hinzugefügt."
    else
        error "❌ Patch 2 (custom_kernels hinzufügen) ist fehlgeschlagen."
        return 1
    fi
else
    log "🔷         -> ⚠️ Patch 2/5 (custom_kernels) scheint bereits angewandt zu sein. Überspringe."
fi

# --- Patch 3: CMakeLists.txt (Alle Include-Pfade injizieren) ---
if [ -f "$CMAKE_LISTS_FILE" ]; then
    log "🔷      -> Patch 3/5: CMakeLists.txt anpassen (Alle Header-Pfade für icpx)."

    local MKL_INCLUDE_PATH="${MKL_ROOT}/include"
    local COMPILER_INCLUDE_PATH="${DPCPP_ROOT}/include"
    local DPCPP_LIB_INCLUDE_PATH="${DPCPP_ROOT}/lib/dpcpp/include"

    local ALL_INCLUDE_FLAGS="-I${MKL_INCLUDE_PATH} -I${COMPILER_INCLUDE_PATH} -I${DPCPP_LIB_INCLUDE_PATH}"
    local PATCH_LINE="    target_compile_options(ggml-sycl PUBLIC \"${ALL_INCLUDE_FLAGS}\")"
    local SEARCH_MARKER="# Add include directories for MKL headers"

    if ! grep -q "${COMPILER_INCLUDE_PATH}" "$CMAKE_LISTS_FILE"; then
        # Spezielle sed-Korrekturen für Pfade/Slashes
        local SED_PATCH_LINE=$(echo "$PATCH_LINE" | sed 's/ /\\ /g; s/[\/&]/\\&/g')
        if sed -i "/${SEARCH_MARKER}/a $SED_PATCH_LINE" "$CMAKE_LISTS_FILE"; then
            log "🔷      -> ✅ Patch 3/5 erfolgreich: Alle Header-Pfade injiziert."
        else
            error "❌ Patch 3 (CMakeLists.txt) ist fehlgeschlagen."
            return 1
        fi
    else
        log "🔷      -> ⚠️ Patch 3/5 (Pfade) scheint bereits angewandt zu sein. Überspringe."
    fi
else
    error "❌ Patch 3 fehlgeschlagen: **CMakeLists.txt** für ggml-sycl nicht gefunden."
    return 1
fi

# --- Patch 4: Flash Attention im ggml-sycl.cpp registrieren (Der entscheidende Fix!) ---
log "🔷      -> Patch 4/5: Flash Attention Dispatch in **ggml-sycl.cpp** injizieren (Robusterer Fix)."

if [ -f "$GGML_SYCL_CPP" ]; then
    # 4a: Deklaration des externen Kernels einfügen (WICHTIG für Linker)
    local FA_REGISTER_CODE=$'// Registriere custom Flash Attention (FA) Kernel\nextern "C" void ggml_sycl_op_flash_attn(ggml_backend_sycl_context * ctx, ggml_tensor * dst, const ggml_tensor * Q, const ggml_tensor * K, const ggml_tensor * V);\n'

    if ! grep -q "ggml_sycl_op_flash_attn" "${GGML_SYCL_CPP}"; then
        # Temporäre Datei mit Deklaration erstellen
        echo "${FA_REGISTER_CODE}" > /tmp/fa_decl.patch

        # Einfügen der Deklaration vor dem ggml_sycl_op_mul_mat_q_k (Referenzpunkt)
        # Hier wird 'awk' anstelle von 'sed' verwendet, da es einfacher ist, eine Datei einzufügen
        awk '/extern "C" void ggml_sycl_op_mul_mat_q_k/ { system("cat /tmp/fa_decl.patch"); } { print }' "${GGML_SYCL_CPP}" > /tmp/ggml-sycl.cpp.new
        mv /tmp/ggml-sycl.cpp.new "${GGML_SYCL_CPP}"

        if [ $? -eq 0 ]; then
            log "🔷         -> Deklaration erfolgreich eingefügt."
        else
            error "❌ Fehler beim Einfügen der FA Deklaration (AWK-Fehler)."
            return 1
        fi
    else
        log "🔷         -> Deklaration ist bereits vorhanden. Überspringe."
    fi

local FA_DISPATCH_CASE=$' case GGML_OP_FLASH_ATTN:\n ggml_sycl_op_flash_attn(ctx, dst, src0, src1, src2);\n break;'

    if ! grep -q "case GGML_OP_FLASH_ATTN:" "${GGML_SYCL_CPP}"; then
        log "🔷         -> Versuche, den Dispatch-Case (FA) mittels AWK einzufügen."

        # Temporäre Datei mit dem Dispatch-Case erstellen
        echo "${FA_DISPATCH_CASE}" > /tmp/fa_dispatch.patch

        # Einfügen des Dispatch-Case VOR dem GGML_OP_MUL_MAT_Q_K (Referenzpunkt)
        # awk '/pattern/ { system("cat file"); } { print }'
        awk '/case GGML_OP_MUL_MAT_Q_K:/ { system("cat /tmp/fa_dispatch.patch"); } { print }' "${GGML_SYCL_CPP}" > /tmp/ggml-sycl.cpp.new
        mv /tmp/ggml-sycl.cpp.new "${GGML_SYCL_CPP}"

        if [ $? -eq 0 ]; then
            log "🔷         -> Dispatch-Case erfolgreich eingefügt."
        else
            error "❌ Fehler beim Einfügen des FA Dispatch-Case (AWK-Fehler)."
            # Wir geben hier absichtlich keine Fehlermeldung aus, da wir den Build trotzdem versuchen wollen
            # return 1
        fi
    else
        log "🔷         -> Dispatch-Case ist bereits vorhanden. Überspringe."
    fi

    log "🔷      -> ✅ Patch 4/5 erfolgreich: Flash Attention Dispatch ist registriert."
else
    error "❌ Patch 4 fehlgeschlagen: **ggml-sycl.cpp** nicht gefunden."
    return 1
fi

--- Patch 5: ggml-sycl Library mit custom Kernel injizieren (OBJECT-Injection) ---

log "🔷      -> Patch 5/5: Injiziere den custom Flash Attention Kernel als OBJECT-Files in ggml-sycl."
local CMAKE_LISTS_FILE="${LLAMA_CPP_DIR}/ggml/src/ggml-sycl/CMakeLists.txt" # Lokale Definition für Patch 5

# 5a: Definiere die Variable FA_OBJECT_FILES
local VAR_LINE="set(FA_OBJECT_FILES \"\$<TARGET_OBJECTS:ggml_flash_attention>\")"
local VAR_SEARCH_MARKER="set(GGML_SYCL_SOURCES"

if ! grep -q "FA_OBJECT_FILES" "$CMAKE_LISTS_FILE"; then
    # Füge die Variable direkt nach der Definition von GGML_SYCL_SOURCES ein.
    local SED_VAR_LINE=$(echo "$VAR_LINE" | sed 's/[\/&]/\\&/g')
    if sed -i "/${VAR_SEARCH_MARKER}/a ${SED_VAR_LINE}" "$CMAKE_LISTS_FILE"; then
         log "🔷      -> 5a/5: FA_OBJECT_FILES Variable erfolgreich definiert."
    else
        error "❌ Patch 5a (Variable) ist fehlgeschlagen."
        return 1
    fi
else
    log "🔷      -> 5a/5: Variable scheint bereits angewandt zu sein. Überspringe."
fi

# 5b: Füge die Variable zur target_sources-Liste hinzu
local TARGET_SEARCH_MARKER="target_sources(ggml-sycl PRIVATE \${GGML_SYCL_SOURCES})"
local NEW_TARGET_SOURCES_LINE="target_sources(ggml-sycl PRIVATE \${GGML_SYCL_SOURCES} \${FA_OBJECT_FILES})"

if grep -q "${TARGET_SEARCH_MARKER}" "$CMAKE_LISTS_FILE" && ! grep -q "\${FA_OBJECT_FILES}" "$CMAKE_LISTS_FILE"; then
    # Ersetze die alte target_sources Zeile durch die neue, erweiterte Zeile.
    local SED_NEW_LINE=$(echo "$NEW_TARGET_SOURCES_LINE" | sed 's/[\/&]/\\&/g')
    local SED_SEARCH_MARKER=$(echo "$TARGET_SEARCH_MARKER" | sed 's/[\/&]/\\&/g')

    if sed -i "s/${SED_SEARCH_MARKER}/${SED_NEW_LINE}/" "$CMAKE_LISTS_FILE"; then
        log "🔷      -> ✅ Patch 5/5 erfolgreich: Flash Attention OBJECT-Files in target_sources injiziert."
    else
        error "❌ Patch 5b (Injection in target_sources) ist fehlgeschlagen."
        return 1
    fi
else
    log "🔷      -> ⚠️ Patch 5/5 (Injection) scheint bereits angewandt zu sein oder Zielzeile nicht gefunden. Überspringe."
fi

success "✅ Alle 5 Patches erfolgreich angewandt."

}

#-- [2] Build-Konfiguration -

configure_build() {
log "🔷 ⚙ Configuring build..."
local FP_MODE="${1:-1}" # Standard 1 (FP16)
local FP_FLAG="-DGGML_SYCL_F16=${FP_MODE}"

if [ ! -d "${BUILD_DIR}" ]; then
    log "   -> Erstelle Build-Verzeichnis: ${BUILD_DIR}"
    mkdir -p "${BUILD_DIR}" || { err "❌ Konnte das Build-Verzeichnis '${BUILD_DIR}' nicht erstellen."; return 1; }
fi

if pushd "${BUILD_DIR}" > /dev/null; then

    log "   -> Starte CMake-Konfiguration (Release, SYCL, FP-Mode: ${FP_FLAG})..."

    # Der neue Kernel wird jetzt automatisch als ggml_flash_attention.a kompiliert!
    cmake "../${LLAMA_CPP_DIR}" \
        -G "Unix Makefiles" \
        -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
        -DGGML_SYCL=ON \
        -DGGML_SYCL_CCACHE=ON \
        -DGGML_SYCL_F16=${FP_MODE} \
        -DGGML_SYCL_FLASH_ATTN=ON \
        -DGGML_SYCL_MKL_SYCL_BATCH_GEMM=1 \
        -DCMAKE_C_COMPILER=icx \
        -DCMAKE_CXX_COMPILER=icpx \
        -DCMAKE_CXX_STANDARD=17

    local CMAKE_STATUS=$?
    popd > /dev/null

    if [ ${CMAKE_STATUS} -ne 0 ]; then
        err "❌ CMake-Konfiguration fehlgeschlagen."
        return 1
    fi

    success "✅ Build-Konfiguration abgeschlossen."
else
    err "❌ Konnte nicht in das Build-Verzeichnis '${BUILD_DIR}' wechseln. Überprüfen Sie die Berechtigungen."
    return 1
fi

}

#-- [3] Kompilieren ----------------------------------------------------------------

compile_project() {
log "🔨 Compiling llama.cpp (SYCL targets) using cmake --build..."
local LOG_FILE="build.log"

log "🔷 📝 Der gesamte Kompilierungs-Output wird in **${BUILD_DIR}/${LOG_FILE}** gespeichert."
log "🔷 🎯 Setze Haupt-Build-Targets auf die ausführbaren Programme: llama-cli und llama-ls-sycl-device"

if pushd "${BUILD_DIR}" > /dev/null; then

    log "🏗 Kompiliere Haupt-Targets..."

    # Wir können das neue Kernel-Target explizit hinzufügen, um sicherzustellen, dass es gebaut wird.
    # Es sollte aber bereits durch die Link-Bibliotheken von llama-cli erfasst werden.
    # Wenn es Probleme gibt, kann 'ggml_flash_attention' hier hinzugefügt werden.
    cmake --build . --config "${CMAKE_BUILD_TYPE}" -j ${NPROC} --target llama-cli llama-ls-sycl-device > "${LOG_FILE}" 2>&1

    local BUILD_STATUS=$?
    popd > /dev/null

    if [ ${BUILD_STATUS} -ne 0 ]; then
        error "❌ Kompilierung der Haupt-Targets (llama-cli) fehlgeschlagen. Überprüfen Sie **${BUILD_DIR}/${LOG_FILE}** für Details."
        return 1
    fi

    success "✅ Kompilierung erfolgreich."
else
    error "❌ Konnte nicht in das Build-Verzeichnis '${BUILD_DIR}' wechseln. Kompilierung nicht möglich."
    return 1
fi

}

#-- [4] Gerät automatisch auswählen ------------------------------------------------

auto_select_device() {
log "🔍 Detecting available SYCL / Level Zero devices ..."

local FULL_LS_PATH="./${BUILD_DIR}/${LS_SYCL_DEVICE_PATH}"

if [ ! -x "${FULL_LS_PATH}" ]; then
    warn "⚠️ llama-ls-sycl-device Binary fehlt im Pfad: ${FULL_LS_PATH}. Fallback auf ARC dGPU."
    export ONEAPI_DEVICE_SELECTOR="level_zero:0"
    DEVICE="ARC" # Standard-Fallback
    return
fi

#-Liste Geräte auf und erfasse den Output-
local DEVICES
# KRITISCHER FIX: Wir nutzen bash -c, um sicherzustellen, dass die Umgebung
# (inkl. LD_LIBRARY_PATH) für die Ausführung korrekt gesetzt ist.
DEVICES=$(bash -c "${FULL_LS_PATH}")

if [ -z "$DEVICES" ]; then
    warn "⚠️ No SYCL devices detected. The system reported an error or zero devices."
    export ONEAPI_DEVICE_SELECTOR="level_zero:0"
    DEVICE="ARC"
    N_GPU_LAYERS=0
    return
fi

# ... Der Rest der Logik ist korrekt und sollte jetzt fehlerfrei arbeiten ...

local ARC_ID
ARC_ID=$(echo "$DEVICES" | grep -i "Intel Arc" | head -n1 | awk '{print $1}')

local IGPU_ID
IGPU_ID=$(echo "$DEVICES" | grep -Ei "Iris|Xe|Graphics" | head -n1 | awk '{print $1}')

local TARGET_LINE=""

if [ -n "$ARC_ID" ]; then
    TARGET_LINE=$(echo "$DEVICES" | grep -i "Intel Arc" | head -n1)
    DEVICE="ARC"

elif [ -n "$IGPU_ID" ]; then
    TARGET_LINE=$(echo "$DEVICES" | grep -Ei "Iris|Xe|Graphics" | head -n1)
    DEVICE="iGPU"

else
    export ONEAPI_DEVICE_SELECTOR="opencl:cpu"
    DEVICE="CPU"
    N_GPU_LAYERS=0
    log "⚠️ No suitable GPU found, CPU fallback enabled."
    return
fi

if [ -n "$TARGET_LINE" ]; then
    local TARGET_ID=$(echo "$TARGET_LINE" | awk '{print $1}')
    export ONEAPI_DEVICE_SELECTOR="level_zero:${TARGET_ID}"
    log "🎯 Using Intel ${DEVICE} (Device ${TARGET_ID})"

    local VRAM_GIB=$(echo "$TARGET_LINE" | grep -oP '\d+(?=M)' | head -n1)
    VRAM_GIB=$((VRAM_GIB / 1024)) # MIB zu GIB

    local LAYER_SIZE_MIB=350
    local VRAM_MIB_CALC=$((VRAM_GIB * 1024))

    N_GPU_LAYERS=$((VRAM_MIB_CALC * 95 / 100 / LAYER_SIZE_MIB))

    if [ "$N_GPU_LAYERS" -gt 99 ]; then
        N_GPU_LAYERS=99
    fi
    if [ "$N_GPU_LAYERS" -lt 1 ]; then
        N_GPU_LAYERS=1
    fi

    log "🧠 Estimated ngl for offloading: **${N_GPU_LAYERS}** layers."
fi

}

#-- [5] SYCL-Geräte prüfen ---------------------------------------------------------

list_sycl_devices() {
log "🔍 Listing SYCL devices ..."
local FULL_LS_PATH="./${BUILD_DIR}/${LS_SYCL_DEVICE_PATH}"

if [ -f "${FULL_LS_PATH}" ]; then
    "${FULL_LS_PATH}"
else
    warn "⚠️ llama-ls-sycl-device binary not found in ${FULL_LS_PATH}. Konnte Geräte nicht auflisten."
fi

}

#-- [6] Modellpfad -----------------------------------------

prepare_model() {
MODEL_PATH=${1:-"models/openhermes-2.5-mistral-7b.Q8_0.gguf"}

mkdir -p models

if [ ! -f "$MODEL_PATH" ]; then
    warn "Model nicht gefunden unter **$MODEL_PATH**. Bitte vor Ausführung herunterladen!"
fi

export MODEL_PATH

}

#-- [7] Inferenz ausführen ---------------------------------------------------------

run_inference() {
local DEFAULT_MODEL_PATH="models/openhermes-2.5-mistral-7b.Q8_0.gguf"
local MODEL_PATH_ARG=${2:-$DEFAULT_MODEL_PATH}
local PROMPT_ARG=${3:-"Hello from SYCL on Intel ARC!"}
local GPU_ID=$(echo "$ONEAPI_DEVICE_SELECTOR" | awk -F':' '{print $2}')
local NGL_SET=${N_GPU_LAYERS:-99}
local FULL_LLAMA_CLI_PATH="./${BUILD_DIR}/${LLAMA_CLI_PATH}"

log "🚀 Running inference on **${DEVICE} (ID: ${GPU_ID})** with ngl=${NGL_SET} using **${FULL_LLAMA_CLI_PATH}**..."

# Check, ob das Binary existiert, bevor es aufgerufen wird
if [ ! -x "${FULL_LLAMA_CLI_PATH}" ]; then
    err "❌ Fehler: Ausführbare Datei **llama-cli** nicht gefunden unter: ${FULL_LLAMA_CLI_PATH}. Build fehlgeschlagen?"
    return 1
fi

ZES_ENABLE_SYSMAN=1 "${FULL_LLAMA_CLI_PATH}" \
    -no-cnv \
    -m "${MODEL_PATH_ARG}" \
    -p "${PROMPT_ARG}" \
    -n 512 \
    -e \
    -ngl -1 \
    --split-mode none \
    --main-gpu "${GPU_ID}"

echo "✅ Inference complete."

}

#-- [8] Main Flow ------------------------------------------------------------------

main() {
local FP_MODE="${1:-1}"

# ⚠️ WICHTIG: Setze RERUN_BUILD standardmäßig auf 1 und überprüfe dann, ob es 0 sein kann.
local RERUN_BUILD=1

prepare_environment

local FULL_LLAMA_CLI_PATH="./${BUILD_DIR}/${LLAMA_CLI_PATH}"
local FULL_LS_PATH="./${BUILD_DIR}/${LS_SYCL_DEVICE_PATH}"

# --- PRÜFUNG: Build-Skip-Logik ---

if [[ -f "${FULL_LLAMA_CLI_PATH}" && -f "${FULL_LS_PATH}" ]]; then
    success "✅ Gefundene Binaries: ${FULL_LLAMA_CLI_PATH} und ${FULL_LS_PATH}"
    log "   -> Überspringe die Schritte Setup, Patch, Configure und Compile."
    RERUN_BUILD=0
else
    warning "⚠️ Keine Binaries gefunden. Starte erstmaligen Build/Rebuild."
    RERUN_BUILD=1
fi

#----------------------------------------

if [[ "$RERUN_BUILD" -eq 1 ]]; then
    log "🏗 Starte Build-Vorgang..."

    setup_project

    patch_llama_cpp

    configure_build "${FP_MODE}"

    compile_project
else

    log "⚙ Update des llama.cpp Repositories und Überprüfung der Patches..."
    setup_project # Für git pull/submodule update
    patch_llama_cpp # Für die Header-Korrektur und die Integration unserer custom_kernels
fi

auto_select_device

list_sycl_devices

prepare_model "${2:-}"

run_inference "${2:-}" "${3:-}"

log "✨ Skript abgeschlossen. Binärdateien sind bereit in **${BUILD_DIR}/${LLAMA_CLI_PATH}** und **${BUILD_DIR}/${LS_SYCL_DEVICE_PATH}**."

}

Skript starten: FP16 (Standard) oder FP32 als erstes Argument

main "${1:-1}" "${2:-}" "${3:-}"

SCHLUSS ENDE EINS VON VIER 1/4

DEMO VERSION ON Youtube:

XAIGPUARC ANGEL VERSION FULL WORKING PLUS EARLY FLASH ATTENTION 1/3 | Ecency