chore: import upstream snapshot with attribution
Copilot Setup Steps / copilot-setup-steps (push) Failing after 2s
Python check requirements.txt / check-requirements (push) Has been cancelled
Python Type-Check / python type-check (push) Has been cancelled
Update Operations Documentation / update-ops-docs (push) Has been cancelled
Check Pre-Tokenizer Hashes / pre-tokenizer-hashes (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Failing after 2s
Python check requirements.txt / check-requirements (push) Has been cancelled
Python Type-Check / python type-check (push) Has been cancelled
Update Operations Documentation / update-ops-docs (push) Has been cancelled
Check Pre-Tokenizer Hashes / pre-tokenizer-hashes (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
"""Shared pytest fixtures for QDC on-device test runners."""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from appium import webdriver
|
||||
|
||||
from utils import options, write_qdc_log
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def driver():
|
||||
return webdriver.Remote(command_executor="http://127.0.0.1:4723/wd/hub", options=options)
|
||||
|
||||
|
||||
def pytest_sessionfinish(session, exitstatus):
|
||||
xml_path = getattr(session.config.option, "xmlpath", None) or "results.xml"
|
||||
if os.path.exists(xml_path):
|
||||
with open(xml_path) as f:
|
||||
write_qdc_log("results.xml", f.read())
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/bin/bash
|
||||
# llama.cpp Hexagon test entry script for QDC Linux IoT (BASH framework).
|
||||
#
|
||||
# Placeholders substituted by run_qdc_jobs.py (--platform linux) before upload:
|
||||
# {MODEL_URL} direct URL to a .gguf model file
|
||||
# {TEST_MODE} bench | backend-ops | all
|
||||
#
|
||||
# QDC extracts the artifact zip to /data/local/tmp/TestContent/ and invokes
|
||||
# this script via: /bin/bash /data/local/tmp/TestContent/run_linux.sh
|
||||
# Any files written under /data/local/tmp/QDC_logs/ are auto-uploaded.
|
||||
|
||||
set +e
|
||||
umask 022
|
||||
|
||||
LOG_DIR=/data/local/tmp/QDC_logs
|
||||
BUNDLE_DIR=/data/local/tmp/TestContent/llama_cpp_bundle
|
||||
MODEL_DIR=/data/local/tmp/gguf
|
||||
MODEL_PATH="$MODEL_DIR/model.gguf"
|
||||
RESULTS_XML="$LOG_DIR/results.xml"
|
||||
|
||||
mkdir -p "$LOG_DIR" "$MODEL_DIR"
|
||||
# Redirect all parent-shell output to script.log so QDC auto-uploads it;
|
||||
# per-case runs still capture their own stdout/stderr into dedicated logs.
|
||||
exec > "$LOG_DIR/script.log" 2>&1
|
||||
|
||||
echo "=== env ==="
|
||||
date -u
|
||||
uname -a
|
||||
pwd
|
||||
|
||||
mount -o rw,remount / 2>/dev/null || true
|
||||
|
||||
cd "$BUNDLE_DIR" || { echo "FATAL: bundle missing at $BUNDLE_DIR"; exit 1; }
|
||||
chmod +x bin/* 2>/dev/null
|
||||
export LD_LIBRARY_PATH="$BUNDLE_DIR/lib:$LD_LIBRARY_PATH"
|
||||
export ADSP_LIBRARY_PATH="$BUNDLE_DIR/lib"
|
||||
export GGML_HEXAGON_EXPERIMENTAL=1
|
||||
|
||||
echo "=== download model ==="
|
||||
MODEL_URL="{MODEL_URL}"
|
||||
if [ -z "$MODEL_URL" ]; then
|
||||
echo "No model URL provided, skipping download"
|
||||
elif [ ! -f "$MODEL_PATH" ]; then
|
||||
curl -L -fS --retry 3 --retry-delay 5 -o "$MODEL_PATH" "$MODEL_URL"
|
||||
curl_rc=$?
|
||||
if [ $curl_rc -ne 0 ]; then
|
||||
echo "FATAL: model download failed (rc=$curl_rc)"
|
||||
exit 1
|
||||
fi
|
||||
ls -la "$MODEL_PATH"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# JUnit XML helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
xml_open() {
|
||||
printf '%s\n' \
|
||||
'<?xml version="1.0" encoding="utf-8"?>' \
|
||||
"<testsuites>" \
|
||||
"<testsuite name=\"llama_cpp_linux\">" \
|
||||
> "$RESULTS_XML"
|
||||
}
|
||||
|
||||
xml_close() {
|
||||
printf '%s\n' '</testsuite>' '</testsuites>' >> "$RESULTS_XML"
|
||||
}
|
||||
|
||||
xml_case_pass() {
|
||||
local classname=$1 name=$2
|
||||
printf '<testcase classname="%s" name="%s"/>\n' "$classname" "$name" >> "$RESULTS_XML"
|
||||
}
|
||||
|
||||
xml_case_fail() {
|
||||
local classname=$1 name=$2 rc=$3 logfile=$4
|
||||
{
|
||||
printf '<testcase classname="%s" name="%s">\n' "$classname" "$name"
|
||||
printf '<failure message="exit %s"><![CDATA[\n' "$rc"
|
||||
tail -c 4096 "$logfile" 2>/dev/null | sed 's/]]>/]] >/g'
|
||||
printf '\n]]></failure>\n</testcase>\n'
|
||||
} >> "$RESULTS_XML"
|
||||
}
|
||||
|
||||
# Map backend name -> "NDEV --device" pair. "none" means no offload (CPU).
|
||||
backend_env() {
|
||||
case "$1" in
|
||||
cpu) echo "0 none" ;;
|
||||
gpu) echo "0 GPUOpenCL" ;;
|
||||
npu) echo "1 HTP0" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
backend_log_name() {
|
||||
case "$1" in
|
||||
cpu) echo "cpu" ;;
|
||||
gpu) echo "gpu" ;;
|
||||
npu) echo "htp" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
backend_device_name() {
|
||||
case "$1" in
|
||||
cpu) echo "none" ;;
|
||||
gpu) echo "GPUOpenCL" ;;
|
||||
npu) echo "HTP0" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Append a diagnostic block when a per-case `timeout N` fires (rc=124). The
|
||||
# naked log file at that point usually just ends mid-OpenCL-init with no
|
||||
# stderr, which is hard to read in CI summaries.
|
||||
note_timeout_if_triggered() {
|
||||
local rc=$1 budget=$2 log=$3
|
||||
[ "$rc" -eq 124 ] || return 0
|
||||
{
|
||||
printf '\n'
|
||||
printf '=== TIMEOUT after %ss ===\n' "$budget"
|
||||
printf 'uptime: '; uptime 2>/dev/null
|
||||
printf 'free -m:\n'; free -m 2>/dev/null
|
||||
printf 'loadavg: '; cat /proc/loadavg 2>/dev/null
|
||||
} >> "$log"
|
||||
}
|
||||
|
||||
completion_extra_args() {
|
||||
case "$1" in
|
||||
cpu) echo "--device none --ctx-size 128 -no-cnv -n 32 --seed 42 --batch-size 128" ;;
|
||||
gpu) echo "--device GPUOpenCL --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 512" ;;
|
||||
npu) echo "--device HTP0 --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 1024" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_completion_case() {
|
||||
local name=$1
|
||||
local parts=($(backend_env "$name"))
|
||||
local ndev=${parts[0]} device=${parts[1]}
|
||||
local device_log_name=$(backend_device_name "$name")
|
||||
local log="$LOG_DIR/llama_completion_${device_log_name}.log"
|
||||
local prompt="$LOG_DIR/bench_prompt.txt"
|
||||
echo 'What is the capital of France?' > "$prompt"
|
||||
local extra
|
||||
extra=$(completion_extra_args "$name")
|
||||
echo "=== [completion:$name] llama-completion --device $device (NDEV=$ndev) ==="
|
||||
timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-completion \
|
||||
-m "$MODEL_PATH" \
|
||||
-f "$prompt" \
|
||||
$extra \
|
||||
> "$log" 2>&1 < /dev/null
|
||||
local rc=$?
|
||||
note_timeout_if_triggered "$rc" 600 "$log"
|
||||
if [ $rc -eq 0 ]; then
|
||||
xml_case_pass "tests.test_bench_posix" "test_llama_completion[$name]"
|
||||
else
|
||||
xml_case_fail "tests.test_bench_posix" "test_llama_completion[$name]" "$rc" "$log"
|
||||
fi
|
||||
}
|
||||
|
||||
run_bench_case() {
|
||||
local name=$1
|
||||
local parts=($(backend_env "$name"))
|
||||
local ndev=${parts[0]} device=${parts[1]}
|
||||
local log_suffix=$(backend_log_name "$name")
|
||||
local log="$LOG_DIR/llama_bench_${log_suffix}.log"
|
||||
echo "=== [bench:$name] llama-bench --device $device (NDEV=$ndev) ==="
|
||||
timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-bench \
|
||||
-m "$MODEL_PATH" \
|
||||
--device "$device" \
|
||||
-ngl 99 \
|
||||
--batch-size 128 \
|
||||
-t 4 \
|
||||
-p 128 \
|
||||
-n 32 \
|
||||
> "$log" 2>&1
|
||||
local rc=$?
|
||||
note_timeout_if_triggered "$rc" 600 "$log"
|
||||
if [ $rc -eq 0 ]; then
|
||||
xml_case_pass "tests.test_bench_posix" "test_llama_bench[$name]"
|
||||
else
|
||||
xml_case_fail "tests.test_bench_posix" "test_llama_bench[$name]" "$rc" "$log"
|
||||
fi
|
||||
}
|
||||
|
||||
run_backend_ops_case() {
|
||||
local dtype=$1
|
||||
local log="$LOG_DIR/backend_ops_${dtype}.log"
|
||||
local pattern
|
||||
case "$dtype" in
|
||||
q4_0)
|
||||
# Matches Android: exclude a known-broken shape on NPU.
|
||||
pattern='^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$'
|
||||
;;
|
||||
*)
|
||||
pattern="type_a=${dtype}"
|
||||
;;
|
||||
esac
|
||||
echo "=== [backend-ops:$dtype] test-backend-ops -b HTP0 -o MUL_MAT ==="
|
||||
timeout 600 env GGML_HEXAGON_NDEV=1 GGML_HEXAGON_HOSTBUF=0 ./bin/test-backend-ops \
|
||||
-b HTP0 -o MUL_MAT -p "$pattern" \
|
||||
> "$log" 2>&1
|
||||
local rc=$?
|
||||
note_timeout_if_triggered "$rc" 600 "$log"
|
||||
if [ $rc -eq 0 ]; then
|
||||
xml_case_pass "tests.test_backend_ops_posix" "test_backend_ops_htp0[$dtype]"
|
||||
else
|
||||
xml_case_fail "tests.test_backend_ops_posix" "test_backend_ops_htp0[$dtype]" "$rc" "$log"
|
||||
fi
|
||||
}
|
||||
|
||||
xml_open
|
||||
|
||||
case "{TEST_MODE}" in
|
||||
bench)
|
||||
for b in cpu gpu npu; do run_completion_case "$b"; done
|
||||
for b in cpu gpu npu; do run_bench_case "$b"; done
|
||||
;;
|
||||
backend-ops)
|
||||
for d in mxfp4 fp16 q4_0; do run_backend_ops_case "$d"; done
|
||||
;;
|
||||
all)
|
||||
for b in cpu gpu npu; do run_completion_case "$b"; done
|
||||
for b in cpu gpu npu; do run_bench_case "$b"; done
|
||||
for d in mxfp4 fp16 q4_0; do run_backend_ops_case "$d"; done
|
||||
;;
|
||||
*)
|
||||
echo "FATAL: unsupported TEST_MODE={TEST_MODE}"
|
||||
;;
|
||||
esac
|
||||
|
||||
xml_close
|
||||
echo "=== done ==="
|
||||
# Host parses results.xml to decide pass/fail.
|
||||
exit 0
|
||||
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
On-device test-backend-ops runner for llama.cpp (HTP0 backend).
|
||||
|
||||
On Android: executed by QDC's Appium test framework on the QDC runner.
|
||||
The runner has ADB access to the allocated device.
|
||||
On Linux: runs test-backend-ops directly via run_linux.sh (BASH framework).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from utils import (
|
||||
BIN_PATH,
|
||||
push_bundle_if_needed,
|
||||
run_script,
|
||||
write_qdc_log,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def install(driver):
|
||||
push_bundle_if_needed(f"{BIN_PATH}/test-backend-ops")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("type_a", ["mxfp4", "fp16", "q4_0"])
|
||||
def test_backend_ops_htp0(type_a):
|
||||
if type_a == "q4_0":
|
||||
pattern = r'^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$'
|
||||
else:
|
||||
pattern = f"type_a={type_a}"
|
||||
|
||||
quoted_pattern = f'"{pattern}"' if type_a == "q4_0" else pattern
|
||||
result = run_script(
|
||||
"run-tool.sh",
|
||||
extra_env={"HB": "0"},
|
||||
extra_args=["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", quoted_pattern],
|
||||
)
|
||||
write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "")
|
||||
assert result.returncode == 0, (
|
||||
f"test-backend-ops type_a={type_a} failed (exit {result.returncode})"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ret = pytest.main(["-s", "--junitxml=results.xml", os.path.realpath(__file__)])
|
||||
if os.path.exists("results.xml"):
|
||||
with open("results.xml") as f:
|
||||
write_qdc_log("results.xml", f.read())
|
||||
sys.exit(ret)
|
||||
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends).
|
||||
|
||||
On Android: calls upstream run-*.sh scripts from llama.cpp/scripts/snapdragon/adb/
|
||||
on the QDC runner host (scripts wrap commands in ``adb shell`` internally).
|
||||
|
||||
On Linux: runs llama-bench directly via run_linux.sh (BASH framework).
|
||||
|
||||
Placeholders replaced at artifact creation time by run_qdc_jobs.py:
|
||||
<<MODEL_URL>> Direct URL to the GGUF model file (downloaded on-device)
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from utils import (
|
||||
BIN_PATH,
|
||||
MODEL_DEVICE_PATH,
|
||||
MODEL_NAME,
|
||||
PROMPT_DIR,
|
||||
push_bundle_if_needed,
|
||||
run_adb_command,
|
||||
run_script,
|
||||
write_qdc_log,
|
||||
)
|
||||
|
||||
MODEL_URL = "<<MODEL_URL>>"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def install(driver):
|
||||
push_bundle_if_needed(f"{BIN_PATH}/llama-cli")
|
||||
run_adb_command(f"mkdir -p /data/local/tmp/gguf {PROMPT_DIR}")
|
||||
run_adb_command(f"echo 'What is the capital of France?' > {PROMPT_DIR}/bench_prompt.txt")
|
||||
check = subprocess.run(
|
||||
["adb", "shell", f"ls {MODEL_DEVICE_PATH}"],
|
||||
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
)
|
||||
if check.returncode != 0:
|
||||
run_adb_command(f'curl -L -J --output {MODEL_DEVICE_PATH} "{MODEL_URL}"')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device",
|
||||
[
|
||||
pytest.param("none", id="cpu"),
|
||||
pytest.param("GPUOpenCL", id="gpu"),
|
||||
pytest.param("HTP0", id="npu"),
|
||||
],
|
||||
)
|
||||
def test_llama_completion(device):
|
||||
result = run_script(
|
||||
"run-completion.sh",
|
||||
extra_env={"D": device, "M": MODEL_NAME},
|
||||
extra_args=["--batch-size", "128", "-n", "128", "--seed", "42",
|
||||
"-f", f"{PROMPT_DIR}/bench_prompt.txt"],
|
||||
)
|
||||
write_qdc_log(f"llama_completion_{device}.log", result.stdout or "")
|
||||
assert result.returncode == 0, (
|
||||
f"llama-completion {device} failed (exit {result.returncode})"
|
||||
)
|
||||
|
||||
|
||||
_DEVICE_LOG_NAME = {"none": "cpu", "GPUOpenCL": "gpu", "HTP0": "htp"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device",
|
||||
[
|
||||
pytest.param("none", id="cpu"),
|
||||
pytest.param("GPUOpenCL", id="gpu"),
|
||||
pytest.param("HTP0", id="npu"),
|
||||
],
|
||||
)
|
||||
def test_llama_bench(device):
|
||||
result = run_script(
|
||||
"run-bench.sh",
|
||||
extra_env={"D": device, "M": MODEL_NAME},
|
||||
extra_args=["--batch-size", "128", "-p", "128", "-n", "32"],
|
||||
)
|
||||
write_qdc_log(f"llama_bench_{_DEVICE_LOG_NAME[device]}.log", result.stdout or "")
|
||||
assert result.returncode == 0, (
|
||||
f"llama-bench {device} failed (exit {result.returncode})"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ret = pytest.main(["-s", "--junitxml=results.xml", os.path.realpath(__file__)])
|
||||
if os.path.exists("results.xml"):
|
||||
with open("results.xml") as f:
|
||||
write_qdc_log("results.xml", f.read())
|
||||
sys.exit(ret)
|
||||
@@ -0,0 +1,143 @@
|
||||
"""Shared helpers for QDC on-device test runners."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from appium.options.common import AppiumOptions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# On-device paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
BUNDLE_PATH = "/data/local/tmp/llama.cpp"
|
||||
BIN_PATH = f"{BUNDLE_PATH}/bin"
|
||||
LIB_PATH = f"{BUNDLE_PATH}/lib"
|
||||
QDC_LOGS_PATH = "/data/local/tmp/QDC_logs"
|
||||
SCRIPTS_DIR = "/qdc/appium"
|
||||
MODEL_NAME = "model.gguf"
|
||||
MODEL_DEVICE_PATH = "/data/local/tmp/gguf/model.gguf"
|
||||
PROMPT_DIR = "/data/local/tmp/scorecard_prompts"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Appium session options
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
options = AppiumOptions()
|
||||
options.set_capability("automationName", "UiAutomator2")
|
||||
options.set_capability("platformName", "Android")
|
||||
options.set_capability("deviceName", os.getenv("ANDROID_DEVICE_VERSION"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shell / process helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def write_qdc_log(filename: str, content: str) -> None:
|
||||
"""Write content as a log file for QDC log collection."""
|
||||
subprocess.run(
|
||||
["adb", "shell", f"mkdir -p {QDC_LOGS_PATH}"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".log", delete=False) as f:
|
||||
f.write(content)
|
||||
tmp_path = f.name
|
||||
try:
|
||||
subprocess.run(
|
||||
["adb", "push", tmp_path, f"{QDC_LOGS_PATH}/{filename}"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
finally:
|
||||
os.unlink(tmp_path)
|
||||
|
||||
|
||||
def ensure_bundle(check_binary: str | None = None) -> None:
|
||||
"""Ensure the llama_cpp_bundle is available on the target device."""
|
||||
push_bundle_if_needed(check_binary or f"{BIN_PATH}/llama-cli")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Android / Linux host helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def run_adb_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess:
|
||||
"""Run a command on-device via ``adb shell`` with exit-code sentinel."""
|
||||
raw = subprocess.run(
|
||||
["adb", "shell", f"{cmd}; echo __RC__:$?"],
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
stdout = raw.stdout
|
||||
returncode = raw.returncode
|
||||
if stdout:
|
||||
lines = stdout.rstrip("\n").split("\n")
|
||||
if lines and lines[-1].startswith("__RC__:"):
|
||||
try:
|
||||
returncode = int(lines[-1][7:])
|
||||
stdout = "\n".join(lines[:-1]) + "\n"
|
||||
except ValueError:
|
||||
pass
|
||||
log.info(stdout)
|
||||
result = subprocess.CompletedProcess(raw.args, returncode, stdout=stdout)
|
||||
if check:
|
||||
assert returncode == 0, f"Command failed (exit {returncode})"
|
||||
return result
|
||||
|
||||
|
||||
def run_script(
|
||||
script: str,
|
||||
extra_env: dict[str, str] | None = None,
|
||||
extra_args: list[str] | None = None,
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""Run an upstream shell script from /qdc/appium/ on the QDC runner host."""
|
||||
env = os.environ.copy()
|
||||
env["GGML_HEXAGON_EXPERIMENTAL"] = "1"
|
||||
if extra_env:
|
||||
env.update(extra_env)
|
||||
cmd = [f"{SCRIPTS_DIR}/{script}"] + (extra_args or [])
|
||||
result = subprocess.run(
|
||||
cmd, env=env,
|
||||
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
)
|
||||
log.info(result.stdout)
|
||||
return result
|
||||
|
||||
|
||||
def adb_shell(cmd: str) -> None:
|
||||
"""Run a command via adb shell (fire-and-forget, no error check)."""
|
||||
subprocess.run(
|
||||
["adb", "shell", "sh", "-c", cmd],
|
||||
capture_output=True, encoding="utf-8", errors="replace", check=False,
|
||||
)
|
||||
|
||||
|
||||
def push_bundle_if_needed(check_binary: str) -> None:
|
||||
"""Push llama_cpp_bundle to the device if check_binary is not already present."""
|
||||
result = subprocess.run(
|
||||
["adb", "shell", f"ls {check_binary}"],
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
subprocess.run(
|
||||
["adb", "push", "/qdc/appium/llama_cpp_bundle/", BUNDLE_PATH],
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
subprocess.run(
|
||||
["adb", "shell", f"find {BUNDLE_PATH}/bin -type f -exec chmod 755 {{}} +"],
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
Reference in New Issue
Block a user