Replace repository with DuckLM runtime
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
from duck_core.model_client import ModelClient
|
||||
|
||||
|
||||
TASKS = [
|
||||
"Скажи коротко, что ты DuckLM.",
|
||||
"Создай tmp/duck_test_note.md с текстом hello duck и прочитай его обратно.",
|
||||
"Посмотри структуру проекта и кратко опиши модули.",
|
||||
"Найди TODO/FIXME в проекте.",
|
||||
"Запусти тесты и кратко объясни результат.",
|
||||
]
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = ModelClient()
|
||||
print("role -> base_url/model")
|
||||
for role, cfg in client._roles.items():
|
||||
print(f"{role} -> {cfg.base_url}/{cfg.model}")
|
||||
started = time.perf_counter()
|
||||
print(f"test_tasks={len(TASKS)}")
|
||||
print("llm_calls=0")
|
||||
print("tool_calls=0")
|
||||
print("json_directive_validity=not_run")
|
||||
print("retry_count=0")
|
||||
print("memory_writes=0")
|
||||
print("experience_record_created=no")
|
||||
print("selected_skill=not_run")
|
||||
print(f"total_runtime_seconds={time.perf_counter() - started:.3f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
LLAMA_DIR="${ROOT_DIR}/vendor/llama.cpp"
|
||||
|
||||
if [[ ! -d "${LLAMA_DIR}/.git" ]]; then
|
||||
git clone --depth 1 https://github.com/ggml-org/llama.cpp "${LLAMA_DIR}"
|
||||
fi
|
||||
|
||||
cmake -S "${LLAMA_DIR}" -B "${LLAMA_DIR}/build" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DGGML_VULKAN=ON \
|
||||
-DGGML_NATIVE=ON \
|
||||
-DLLAMA_BUILD_TESTS=OFF
|
||||
|
||||
cmake --build "${LLAMA_DIR}/build" --config Release --target llama-server -j "$(nproc)"
|
||||
|
||||
"${LLAMA_DIR}/build/bin/llama-server" --list-devices
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${1:-http://127.0.0.1:8081/v1}"
|
||||
|
||||
curl --noproxy "*" -fsS "${BASE_URL}/models" >/dev/null
|
||||
|
||||
echo "OK: ${BASE_URL}"
|
||||
Executable
+260
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
ENV_KEYS=(
|
||||
DUCK_LLAMA_SERVER_BIN
|
||||
DUCK_MAIN_MODEL_PATH
|
||||
DUCK_MAIN_PORT
|
||||
DUCK_CTX_SIZE
|
||||
DUCK_N_GPU_LAYERS
|
||||
DUCK_LLAMA_DEVICE
|
||||
DUCK_PARALLEL
|
||||
DUCK_LLAMA_PID_FILE
|
||||
DUCK_LLAMA_LOG_FILE
|
||||
DUCK_LLAMA_EXTRA_ARGS
|
||||
DUCK_HOST
|
||||
)
|
||||
declare -A ENV_OVERRIDES=()
|
||||
for key in "${ENV_KEYS[@]}"; do
|
||||
if [[ -v "${key}" ]]; then
|
||||
ENV_OVERRIDES["${key}"]="${!key}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -f "${ROOT_DIR}/.env" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT_DIR}/.env"
|
||||
set +a
|
||||
fi
|
||||
for key in "${!ENV_OVERRIDES[@]}"; do
|
||||
export "${key}=${ENV_OVERRIDES[${key}]}"
|
||||
done
|
||||
|
||||
ACTION="${1:-start}"
|
||||
PID_FILE="${DUCK_LLAMA_PID_FILE:-${ROOT_DIR}/data/llama-main.pid}"
|
||||
LOG_FILE="${DUCK_LLAMA_LOG_FILE:-${ROOT_DIR}/data/llama-main.log}"
|
||||
BASE_URL="http://${DUCK_HOST:-127.0.0.1}:${DUCK_MAIN_PORT:-8081}/v1"
|
||||
|
||||
resolve_project_path() {
|
||||
local value="$1"
|
||||
if [[ "${value}" == /* ]]; then
|
||||
printf '%s\n' "${value}"
|
||||
else
|
||||
printf '%s\n' "${ROOT_DIR}/${value#./}"
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/llama/start_main.sh <command>
|
||||
|
||||
Commands:
|
||||
start Start llama-server in the background
|
||||
stop Stop the managed llama-server process
|
||||
restart Stop and start llama-server
|
||||
status Print process and HTTP health status
|
||||
logs Show logs; use --follow/-f and --lines N
|
||||
help Show this help
|
||||
|
||||
Environment:
|
||||
DUCK_LLAMA_SERVER_BIN Path to llama-server binary
|
||||
DUCK_MAIN_MODEL_PATH Path to GGUF model
|
||||
DUCK_HOST Bind host, default 127.0.0.1
|
||||
DUCK_MAIN_PORT Port, default 8081
|
||||
DUCK_CTX_SIZE Context size, default 65536
|
||||
DUCK_N_GPU_LAYERS GPU layers, default auto
|
||||
DUCK_LLAMA_DEVICE Device name, for example Vulkan0
|
||||
DUCK_PARALLEL Server slots, default 1
|
||||
DUCK_LLAMA_PID_FILE PID file path
|
||||
DUCK_LLAMA_LOG_FILE Log file path
|
||||
DUCK_LLAMA_EXTRA_ARGS Extra llama-server args
|
||||
EOF
|
||||
}
|
||||
|
||||
is_running() {
|
||||
[[ -f "${PID_FILE}" ]] || return 1
|
||||
local pid
|
||||
pid="$(cat "${PID_FILE}")"
|
||||
[[ "${pid}" =~ ^[0-9]+$ ]] || return 1
|
||||
kill -0 "${pid}" 2>/dev/null
|
||||
}
|
||||
|
||||
pid_value() {
|
||||
if [[ -f "${PID_FILE}" ]]; then
|
||||
cat "${PID_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
if is_running; then
|
||||
local pid
|
||||
pid="$(pid_value)"
|
||||
echo "llama-server running: pid=${pid}"
|
||||
if command -v curl >/dev/null 2>&1 && curl --noproxy "*" -fsS "${BASE_URL}/models" >/dev/null 2>&1; then
|
||||
echo "HTTP health: ok (${BASE_URL})"
|
||||
else
|
||||
echo "HTTP health: not ready (${BASE_URL})"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -f "${PID_FILE}" ]]; then
|
||||
echo "llama-server not running; removing stale pid file ${PID_FILE}"
|
||||
rm -f "${PID_FILE}"
|
||||
else
|
||||
echo "llama-server not running"
|
||||
fi
|
||||
return 3
|
||||
}
|
||||
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "llama-server already running: pid=$(pid_value)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
: "${DUCK_MAIN_MODEL_PATH:?DUCK_MAIN_MODEL_PATH is required}"
|
||||
|
||||
mkdir -p "$(dirname "${PID_FILE}")" "$(dirname "${LOG_FILE}")"
|
||||
rm -f "${PID_FILE}"
|
||||
|
||||
local llama_bin model_path
|
||||
llama_bin="${DUCK_LLAMA_SERVER_BIN:-llama-server}"
|
||||
if [[ "${llama_bin}" == */* ]]; then
|
||||
llama_bin="$(resolve_project_path "${llama_bin}")"
|
||||
fi
|
||||
model_path="$(resolve_project_path "${DUCK_MAIN_MODEL_PATH}")"
|
||||
local command=(
|
||||
"${llama_bin}"
|
||||
-m "${model_path}"
|
||||
--alias local-main
|
||||
--host "${DUCK_HOST:-127.0.0.1}"
|
||||
--port "${DUCK_MAIN_PORT:-8081}"
|
||||
-c "${DUCK_CTX_SIZE:-65536}"
|
||||
--parallel "${DUCK_PARALLEL:-1}"
|
||||
-ngl "${DUCK_N_GPU_LAYERS:-auto}"
|
||||
--flash-attn on
|
||||
--cache-prompt
|
||||
--metrics
|
||||
)
|
||||
if [[ -n "${DUCK_LLAMA_DEVICE:-}" ]]; then
|
||||
command+=(--device "${DUCK_LLAMA_DEVICE}")
|
||||
fi
|
||||
if [[ -n "${DUCK_LLAMA_EXTRA_ARGS:-}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
local extra_args=( ${DUCK_LLAMA_EXTRA_ARGS} )
|
||||
command+=("${extra_args[@]}")
|
||||
fi
|
||||
|
||||
echo "Starting llama-server..."
|
||||
echo "Command: ${command[*]}" >> "${LOG_FILE}"
|
||||
if command -v setsid >/dev/null 2>&1; then
|
||||
nohup setsid "${command[@]}" >> "${LOG_FILE}" 2>&1 &
|
||||
else
|
||||
nohup "${command[@]}" >> "${LOG_FILE}" 2>&1 &
|
||||
fi
|
||||
local pid=$!
|
||||
echo "${pid}" > "${PID_FILE}"
|
||||
sleep 0.2
|
||||
|
||||
if is_running; then
|
||||
echo "llama-server started: pid=${pid}"
|
||||
echo "Log: ${LOG_FILE}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "llama-server failed to start. See ${LOG_FILE}" >&2
|
||||
rm -f "${PID_FILE}"
|
||||
return 1
|
||||
}
|
||||
|
||||
stop() {
|
||||
if ! is_running; then
|
||||
rm -f "${PID_FILE}"
|
||||
echo "llama-server not running"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local pid
|
||||
pid="$(pid_value)"
|
||||
echo "Stopping llama-server: pid=${pid}"
|
||||
kill "${pid}" 2>/dev/null || true
|
||||
|
||||
for _ in {1..30}; do
|
||||
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||
rm -f "${PID_FILE}"
|
||||
echo "llama-server stopped"
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
echo "llama-server did not stop after SIGTERM; sending SIGKILL"
|
||||
kill -9 "${pid}" 2>/dev/null || true
|
||||
rm -f "${PID_FILE}"
|
||||
echo "llama-server stopped"
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
logs() {
|
||||
local follow=0
|
||||
local lines=100
|
||||
shift || true
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-f|--follow)
|
||||
follow=1
|
||||
shift
|
||||
;;
|
||||
--lines)
|
||||
lines="${2:?--lines requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown logs argument: $1" >&2
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$(dirname "${LOG_FILE}")"
|
||||
touch "${LOG_FILE}"
|
||||
if [[ "${follow}" == "1" ]]; then
|
||||
tail -n "${lines}" -f "${LOG_FILE}"
|
||||
else
|
||||
tail -n "${lines}" "${LOG_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${ACTION}" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
logs)
|
||||
logs "$@"
|
||||
;;
|
||||
help|-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: ${ACTION}" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
ENV_KEYS=(
|
||||
DUCK_LLAMA_SERVER_BIN
|
||||
DUCK_MAIN_MODEL_PATH
|
||||
DUCK_MTP_MODEL_PATH
|
||||
DUCK_MAIN_MTP_PORT
|
||||
DUCK_CTX_SIZE
|
||||
DUCK_N_GPU_LAYERS
|
||||
DUCK_LLAMA_DEVICE
|
||||
DUCK_PARALLEL
|
||||
DUCK_MTP_FLAGS
|
||||
DUCK_HOST
|
||||
)
|
||||
declare -A ENV_OVERRIDES=()
|
||||
for key in "${ENV_KEYS[@]}"; do
|
||||
if [[ -v "${key}" ]]; then
|
||||
ENV_OVERRIDES["${key}"]="${!key}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -f "${ROOT_DIR}/.env" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT_DIR}/.env"
|
||||
set +a
|
||||
fi
|
||||
for key in "${!ENV_OVERRIDES[@]}"; do
|
||||
export "${key}=${ENV_OVERRIDES[${key}]}"
|
||||
done
|
||||
|
||||
: "${DUCK_MAIN_MODEL_PATH:?DUCK_MAIN_MODEL_PATH is required}"
|
||||
ACTION="${1:-start}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/llama/start_thinker_mtp_experimental.sh <command>
|
||||
|
||||
Commands:
|
||||
start Start experimental MTP/speculative llama-server in foreground
|
||||
check Check whether the current llama-server binary exposes draft-mtp flags
|
||||
help Show this help
|
||||
EOF
|
||||
}
|
||||
|
||||
resolve_project_path() {
|
||||
local value="$1"
|
||||
if [[ "${value}" == /* ]]; then
|
||||
printf '%s\n' "${value}"
|
||||
else
|
||||
printf '%s\n' "${ROOT_DIR}/${value#./}"
|
||||
fi
|
||||
}
|
||||
|
||||
LLAMA_BIN="${DUCK_LLAMA_SERVER_BIN:-llama-server}"
|
||||
if [[ "${LLAMA_BIN}" == */* ]]; then
|
||||
LLAMA_BIN="$(resolve_project_path "${LLAMA_BIN}")"
|
||||
fi
|
||||
MAIN_MODEL_PATH="$(resolve_project_path "${DUCK_MAIN_MODEL_PATH}")"
|
||||
|
||||
HELP_TEXT="$("${LLAMA_BIN}" --help 2>&1 || true)"
|
||||
if ! grep -qi "draft-mtp" <<< "${HELP_TEXT}"; then
|
||||
echo "This llama-server build does not expose draft-mtp speculative decoding."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "${ACTION}" in
|
||||
check)
|
||||
echo "OK: draft-mtp speculative decoding is exposed by ${LLAMA_BIN}"
|
||||
exit 0
|
||||
;;
|
||||
help|-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
start)
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: ${ACTION}" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
command=(
|
||||
"${LLAMA_BIN}"
|
||||
-m "${MAIN_MODEL_PATH}"
|
||||
--alias local-main-mtp
|
||||
--host "${DUCK_HOST:-127.0.0.1}"
|
||||
--port "${DUCK_MAIN_MTP_PORT:-8085}"
|
||||
-c "${DUCK_CTX_SIZE:-65536}"
|
||||
--parallel "${DUCK_PARALLEL:-1}"
|
||||
-ngl "${DUCK_N_GPU_LAYERS:-auto}"
|
||||
--flash-attn on
|
||||
--cache-prompt
|
||||
--metrics
|
||||
--spec-type draft-mtp
|
||||
)
|
||||
|
||||
if [[ -n "${DUCK_LLAMA_DEVICE:-}" ]]; then
|
||||
command+=(--device "${DUCK_LLAMA_DEVICE}")
|
||||
fi
|
||||
|
||||
if [[ -n "${DUCK_MTP_MODEL_PATH:-}" ]]; then
|
||||
command+=(--model-draft "$(resolve_project_path "${DUCK_MTP_MODEL_PATH}")")
|
||||
fi
|
||||
|
||||
if [[ -n "${DUCK_MTP_FLAGS:-}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
extra_args=( ${DUCK_MTP_FLAGS} )
|
||||
command+=("${extra_args[@]}")
|
||||
fi
|
||||
|
||||
exec "${command[@]}"
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
VENV_PYTHON="$ROOT_DIR/.venv/bin/python"
|
||||
PID_FILE="$ROOT_DIR/data/runtime/server.pid"
|
||||
LOG_FILE="$ROOT_DIR/data/runtime/server.log"
|
||||
HOST="${HOST:-127.0.0.1}"
|
||||
PORT="${PORT:-8000}"
|
||||
|
||||
mkdir -p "$ROOT_DIR/data/runtime"
|
||||
|
||||
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}$ROOT_DIR/.venv/lib/python3.13/site-packages/llama_cpp/lib"
|
||||
export GGML_VULKAN=1
|
||||
|
||||
is_running() {
|
||||
if [[ -f "$PID_FILE" ]]; then
|
||||
local pid
|
||||
pid="$(cat "$PID_FILE")"
|
||||
if kill -0 "$pid" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
start_server() {
|
||||
if is_running; then
|
||||
echo "Server already running with PID $(cat "$PID_FILE")"
|
||||
exit 0
|
||||
fi
|
||||
nohup "$VENV_PYTHON" -m uvicorn main:app --host "$HOST" --port "$PORT" >"$LOG_FILE" 2>&1 &
|
||||
echo $! >"$PID_FILE"
|
||||
echo "Started server on http://$HOST:$PORT with PID $(cat "$PID_FILE")"
|
||||
echo "Log: $LOG_FILE"
|
||||
}
|
||||
|
||||
stop_server() {
|
||||
if ! is_running; then
|
||||
echo "Server is not running"
|
||||
rm -f "$PID_FILE"
|
||||
exit 0
|
||||
fi
|
||||
local pid
|
||||
pid="$(cat "$PID_FILE")"
|
||||
kill "$pid"
|
||||
rm -f "$PID_FILE"
|
||||
echo "Stopped server PID $pid"
|
||||
}
|
||||
|
||||
status_server() {
|
||||
if is_running; then
|
||||
echo "Server is running with PID $(cat "$PID_FILE") on http://$HOST:$PORT"
|
||||
else
|
||||
echo "Server is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
start)
|
||||
start_server
|
||||
;;
|
||||
stop)
|
||||
stop_server
|
||||
;;
|
||||
restart)
|
||||
stop_server || true
|
||||
start_server
|
||||
;;
|
||||
status)
|
||||
status_server
|
||||
;;
|
||||
logs)
|
||||
touch "$LOG_FILE"
|
||||
tail -n 50 -f "$LOG_FILE"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|logs}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
|
||||
curl -fsS "${BASE_URL}/health"
|
||||
|
||||
curl -fsS -X POST "${BASE_URL}/v1/chat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"message": "Скажи коротко, что ты DuckLM",
|
||||
"debug": true
|
||||
}'
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
curl -fsS "${BASE_URL}/v1/experience"
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
|
||||
RESPONSE="$(curl -fsS -X POST "${BASE_URL}/v1/chat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"message": "Создай tmp/duck_test_note.md с текстом hello duck и прочитай его обратно",
|
||||
"workspace": "./workspace",
|
||||
"debug": true
|
||||
}')"
|
||||
|
||||
echo "${RESPONSE}"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
curl -fsS "${BASE_URL}/v1/memory/search?q=duck"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
curl -fsS "${BASE_URL}/v1/models/roles"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${DUCK_API_URL:-http://127.0.0.1:8000}"
|
||||
curl -fsS "${BASE_URL}/v1/skills"
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
python3 - <<'PY'
|
||||
import asyncio
|
||||
from duck_core.tools.shell_exec_safe import ShellExecSafeTool
|
||||
|
||||
async def main():
|
||||
result = await ShellExecSafeTool(".").run({"command": "rm -rf ."})
|
||||
assert not result.ok
|
||||
print("OK: dangerous command blocked")
|
||||
|
||||
asyncio.run(main())
|
||||
PY
|
||||
Reference in New Issue
Block a user