Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .ci/docker/ci_commit_pins/optimum-executorch.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0123293118efb08ac4ffc4fefe9d330201465c93
de4f3c4978b4d36cc0bb8f87c6877a4a040d7ae7
2 changes: 1 addition & 1 deletion .ci/docker/ci_commit_pins/pytorch.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7a064ed3eafa43f17412d434b395240c727b3000
7a79b41e29a790ebb4b530eb98a89381e2d7de29
222 changes: 185 additions & 37 deletions .ci/scripts/setup-samsung-linux-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,209 @@

set -ex

API_KEY=$SAMSUNG_AI_LITECORE_KEY
if [[ -z "${API_KEY}" ]]; then
echo "ERROR: It didn't set up SAMSUNG_AI_LITECORE_KEY." >&2
exit 1
fi

OS_NAME="Ubuntu 22.04"
LITECORE_BASE="https://soc-developer.semiconductor.samsung.com/api/v1/resource/ai-litecore/download"
DEVICEFARM_BASE="https://soc-developer.semiconductor.samsung.com/api/v1/resource/remotelab/download"

parse_url() {
local json="$1"
if command -v jq >/dev/null 2>&1; then
jq -r '.data // empty' <<<"$json"
else
sed -n 's/.*"data":[[:space:]]*"\([^"]*\)".*/\1/p' <<<"$json"
fi
}

download_ai_lite_core() {
API_BASE="https://soc-developer.semiconductor.samsung.com/api/v1/resource/ai-litecore/download"
API_KEY=$SAMSUNG_AI_LITECORE_KEY

VERSION="0.7"
OS_NAME="Ubuntu 22.04"
OUT_FILE="/tmp/exynos-ai-litecore-v${VERSION}.tar.gz"
TARGET_PATH="/tmp/exynos_ai_lite_core"

mkdir -p ${TARGET_PATH}
# Presigned issue URL
JSON_RESP=$(curl -sS -G \
--location --fail --retry 3 \
download_and_extract() {
local base_url="$1"
local version="$2"
local out_dir="$3"
local out_file="$4"

local resp
resp=$(curl -fsSL -G \
-H "apikey: ${API_KEY}" \
--data-urlencode "version=${VERSION}" \
--data-urlencode "version=${version}" \
--data-urlencode "os=${OS_NAME}" \
"${API_BASE}")
"${base_url}")

local download_url
download_url=$(parse_url "$resp")
if [[ -z "${download_url}" ]]; then
echo "ERROR: It failed to download from ${base_url} ."
echo "Response: $resp" >&2
exit 1
fi

curl -fsSL -L --retry 3 -o "${out_file}" "${download_url}"
echo "Download completed: ${out_file}"

DOWNLOAD_URL=$(echo "$JSON_RESP" | sed -n 's/.*"data":[[:space:]]*"\([^"]*\)".*/\1/p')
mkdir -p "${out_dir}"
case "${out_file##*.}" in
tar|tgz|gz)
echo "Extracting TAR.GZ..."
tar -C "${out_dir}" --strip-components=1 -xzvf "${out_file}"
;;

if [[ -z "$DOWNLOAD_URL" ]]; then
echo "Failed to extract download URL"
echo "$JSON_RESP"
zip)
echo "Extracting ZIP..."
unzip -q -d "${out_dir}" "${out_file}"
;;

*)
exit 1
;;
esac
echo "Extracted to: ${out_dir}"
}

download_ai_lite_core() {
local litecore_version="${1:-1.0}"
local litecore_out="/tmp/exynos-ai-litecore-v${litecore_version}.tar.gz"
local litecore_dir="/tmp/exynos_ai_lite_core"

download_and_extract \
"${LITECORE_BASE}" \
"${litecore_version}" \
"${litecore_dir}" \
"${litecore_out}"

export EXYNOS_AI_LITECORE_ROOT="${litecore_dir}"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:${EXYNOS_AI_LITECORE_ROOT}/lib/x86_64-linux"
}

install_devicefarm_cli() {
local cli_version="${1:-beta-1.0.8}"
local cli_out="/tmp/devicefarm-cli-v${cli_version}.zip"
local cli_dir="/tmp/devicefarm_cli"

download_and_extract \
"${DEVICEFARM_BASE}" \
"${cli_version}" \
"${cli_dir}" \
"${cli_out}"

export PATH="${PATH%:}:${cli_dir}"
chmod +x "${cli_dir}/devicefarm-cli"
}

reserve_if_needed() {
# Set default value
export DEVICE_RESERVED=0

if ! command -v devicefarm-cli >/dev/null 2>&1; then
echo "[WARN] devicefarm-cli is not installed." >&2
return 1
fi

# Download LiteCore
curl -sS -L --fail --retry 3 \
--output "$OUT_FILE" \
"$DOWNLOAD_URL"
local raw_info info_lines
raw_info="$(devicefarm-cli -I)"

info_lines="$(printf '%s\n' "$raw_info" | grep -v '^\\[INFO\\]')"

echo "Download done: $OUT_FILE"
local found_count

found_count=$(printf '%s\n' "$info_lines" \
| grep -Eo 'Found available reservations *: *[0-9]+' \
| grep -Eo '[0-9]+')
[[ -z "$found_count" ]] && found_count=0

tar -C "${TARGET_PATH}" --strip-components=1 -xzvf "${OUT_FILE}"
echo "[INFO] Current Reserved Count: $found_count"

local THRESHOLD_SECONDS=12600
local any_below_threshold=0

if (( found_count > 0 )); then
local table_body
table_body=$(printf '%s\n' "$info_lines" | sed -n '2,$p')

while IFS= read -r line; do
if [[ "$line" =~ ^[0-9]+[[:space:]]+([0-9]{1,2}:[0-9]{2}:[0-9]{2}) ]]; then
local time_str="${BASH_REMATCH[1]}"
IFS=: read -r hh mm ss <<<"$time_str"
(( seconds = 10#$hh * 3600 + 10#$mm * 60 + 10#$ss ))
if (( seconds <= THRESHOLD_SECONDS )); then
any_below_threshold=1
break
fi
fi
done <<<"$table_body"
else
any_below_threshold=1
fi

export EXYNOS_AI_LITECORE_ROOT=${TARGET_PATH}
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:${EXYNOS_AI_LITECORE_ROOT}/lib/x86_64-linux
if (( any_below_threshold )); then
echo "[INFO] Reserving now."
if ! devicefarm-cli -R; then
echo "::warning::Failed to reserve a device. No devices are currently available." >&2
echo "[WARN] Device reservation failed - continuing without device." >&2
return 0
fi
else
echo "[INFO] Don't need to be reserved."
fi

local info_after reservation_id max_seconds=0 max_id

info_after="$(devicefarm-cli -I)"

local body_after
body_after=$(printf '%s\n' "$info_after" | grep -v '^\\[INFO\\]' | sed -n '2,$p')

while IFS= read -r line; do
if [[ "$line" =~ ^[0-9]+[[:space:]]+([0-9]{1,2}:[0-9]{2}:[0-9]{2})[[:space:]].*([0-9a-f-]{36})$ ]]; then
local time_str="${BASH_REMATCH[1]}"
local id="${BASH_REMATCH[2]}"
IFS=: read -r hh mm ss <<<"$time_str"
(( seconds = 10#$hh * 3600 + 10#$mm * 60 + 10#$ss ))
if (( seconds > max_seconds )); then
max_seconds=$seconds
max_id=$id
fi
fi
done <<<"$body_after"

reservation_id=$max_id

if [[ -n "$reservation_id" ]]; then
devicefarm-cli -C "$reservation_id"
devicefarm-cli -E "ls /"
export DEVICE_RESERVED=1
echo "[INFO] Device successfully reserved and connected."
else
echo "::warning::No available devices found." >&2
echo "[WARN] There is no available devices."
fi
}

install_enn_backend() {
NDK_INSTALLATION_DIR=/opt/ndk
rm -rf "${NDK_INSTALLATION_DIR}" && sudo mkdir -p "${NDK_INSTALLATION_DIR}"
ANDROID_NDK_VERSION=r28c
local ndk_dir="/opt/ndk"
local ndk_version="r28c"

if [[ ! -d "${ndk_dir}" ]]; then
sudo mkdir -p "${ndk_dir}"
sudo chown "$(whoami)":"$(whoami)" "${ndk_dir}"
fi

export ANDROID_NDK_ROOT="${ndk_dir}"
echo "NDK will be installed/used at: ${ANDROID_NDK_ROOT}"

# build Exynos backend
export ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT:-/opt/ndk}
bash backends/samsung/build.sh --build all
# set env variable
export EXECUTORCH_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
export PYTHONPATH=${PYTHONPATH:-}:${EXECUTORCH_ROOT}/..

export EXECUTORCH_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
export PYTHONPATH="${PYTHONPATH:-}:${EXECUTORCH_ROOT}/.."
}

AI_LITE_CORE_VERSION=0.7.0
litecore_ver="1.0"
devicefarm_ver="beta-1.0.8"

download_ai_lite_core ${AI_LITE_CORE_VERSION}
download_ai_lite_core ${litecore_ver}
install_devicefarm_cli "${devicefarm_ver}"
install_enn_backend
reserve_if_needed
34 changes: 32 additions & 2 deletions .ci/scripts/test_huggingface_optimum_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,35 @@ def test_text_generation(model_id, model_dir, recipe, *, quantize=True, run_only
assert check_causal_lm_output_quality(model_id, generated_tokens) is True


def get_tokenizer_path(model_dir: str, saved_files: tuple) -> str:
"""
Determine the tokenizer path based on files saved by tokenizer.save_pretrained().

Args:
model_dir: The directory where tokenizer files were saved
saved_files: Tuple of file paths returned by tokenizer.save_pretrained()

Returns:
The path to use for loading the tokenizer (either a specific file or directory)

Raises:
ValueError: If no supported tokenizer file format is found
"""
saved_filenames = {Path(f).name for f in saved_files}

if "tokenizer.model" in saved_filenames:
return f"{model_dir}/tokenizer.model"

if "tokenizer.json" in saved_filenames:
return model_dir

# No supported tokenizer format found
raise ValueError(
f"Unsupported tokenizer format. Expected 'tokenizer.model' (SentencePiece) "
f"or 'tokenizer.json' (HuggingFace) but found: {saved_filenames}"
)


def test_llm_with_image_modality(
model_id, model_dir, recipe, *, quantize=True, run_only=False
):
Expand All @@ -196,7 +225,8 @@ def test_llm_with_image_modality(
cli_export(command, model_dir)

tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.save_pretrained(model_dir)
saved_files = tokenizer.save_pretrained(model_dir)
tokenizer_path = get_tokenizer_path(model_dir, saved_files)

# input
processor = AutoProcessor.from_pretrained(model_id)
Expand Down Expand Up @@ -232,7 +262,7 @@ def test_llm_with_image_modality(

from executorch.extension.llm.runner import GenerationConfig, MultimodalRunner

runner = MultimodalRunner(f"{model_dir}/model.pte", f"{model_dir}/tokenizer.model")
runner = MultimodalRunner(f"{model_dir}/model.pte", tokenizer_path)
generated_text = runner.generate_text_hf(
inputs,
GenerationConfig(max_new_tokens=128, temperature=0, echo=False),
Expand Down
2 changes: 1 addition & 1 deletion .ci/scripts/test_model_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ case "$HF_MODEL" in
MODEL_NAME="voxtral"
RUNNER_TARGET="voxtral_runner"
RUNNER_PATH="voxtral"
EXPECTED_OUTPUT="poem"
EXPECTED_OUTPUT="existence"
PREPROCESSOR="voxtral_preprocessor.pte"
TOKENIZER_URL="https://huggingface.co/mistralai/Voxtral-Mini-3B-2507/resolve/main" # @lint-ignore
TOKENIZER_FILE="tekken.json"
Expand Down
4 changes: 1 addition & 3 deletions .ci/scripts/wheel/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ def test_model_xnnpack(model: Model, quantize: bool) -> None:

if quantize:
quant_type = MODEL_NAME_TO_OPTIONS[str(model)].quantization
model_instance = torch.export.export_for_training(
model_instance, example_inputs
)
model_instance = torch.export.export(model_instance, example_inputs)
model_instance = quantize_xnn(
model_instance.module(), example_inputs, quant_type
)
Expand Down
Loading